home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 4 / ETO Development Tools 4.iso / Tools - Objects / MacApp / MacApp 3.0a2 / Libraries / UDebug.inc1.p < prev    next >
Encoding:
Text File  |  1991-05-01  |  89.8 KB  |  3,659 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n+]}
  3. { UDebug.inc1.p }
  4. { Copyright © 1985-1989 by Apple Computer, Inc.  All rights reserved. }
  5.  
  6. USES
  7.     {
  8.     • List units defining any constants, types or classes that are required for your implementation
  9.     section (e.g. Packages or Errors)
  10.     • Also list units defining the classes that you declared EXTERNAL in the interface section
  11.     or wish to use in the implementation section.
  12.     • Also list the units required by the interfaces of the above units.
  13.     }
  14.     OSUtils, TextEdit, Memory, UMacAppUniversal, UPascalObject, UObject, UList, UStream, AppleEvents, UEvent, UCommand, UEvtHandler, Editions, Dialogs,
  15.      UApplication, UDocument, Balloons,UAdorners, UView, UWindow, UFailure, UMacAppUtilities, UPatch, UMemory,
  16.      UMacAppGlobals, UGeometry, UErrorMgr, Menus, UMenuMgr, Errors, ToolUtils, Packages, Fonts,
  17.      Script, GestaltEqu, UTranscriptView, UInspector, Desk, DiskInit, Retrace, Resources,
  18.      PasLibIntf, OSEvents, Perf, DisAsmLookUp, Notification, Processes, SysEqu, Devices, ULoMem;
  19.  
  20. {$IFC NOT qDebugTheDebugger}
  21. {$W+}
  22. {$R-}
  23. {$Init-}
  24. {$OV-}
  25. {$ENDC}
  26. {$IFC qNames}
  27. {$D+}
  28. {$ENDC}
  29.  
  30. {$IFC UNDEFINED IncludeDisassembler}
  31. {$SETC IncludeDisassembler := FALSE}                    { Don't automatically include in this
  32.                                                          version }
  33. {$ENDC}
  34.  
  35. CONST
  36.     { Message types from the nub }
  37.     kReadableText        = 'text';                        { Text from unknown source. Intended to
  38.                                                          notify user }
  39.     kRequestUserInput    = 'rui ';                        { Do whatever needs to be done to get user's
  40.                                                          attn }
  41.     kEnteredDebugger    = 'Dent';                        { The debugger nub was entered by the program }
  42.  
  43.     { Message types to the nub }
  44.     kKeyStroke            = 'keys';                        { User keystroke in response to an inquiry }
  45.     kEnterMacsBug        = 'EMac';                        { Have the nub enter macsbug }
  46.     kExitToShell        = 'EShl';                        { Have the nub ExitToShell }
  47.     kStatus             = 'Stat';
  48.     kSetBreak            = 'SetB';
  49.     kClearBreak         = 'ClrB';
  50.     kDisplayMem         = 'DspM';
  51.     kFieldsAsHex        = 'FldH';
  52.     kGo                 = 'Go!!';
  53.     kStepOver            = 'StpO';
  54.     kStepInto            = 'StpI';
  55.     kHeapCmd            = 'Heap';
  56.     kInspect            = 'Insp';
  57.     kLocals             = 'Lcls';
  58.     kMore                = 'More';
  59.     kParameters         = 'Parm';
  60.     kPerfCommand        = 'Perf';
  61.     kRecentPC            = 'Recn';
  62.     kStack                = 'Stak';
  63.     kSignalFailure        = 'Fail';
  64.     kTrace                = 'Trce';
  65.     kFlags                = 'Flag';
  66.     kAllClasses         = 'AllC';
  67.     kDisasm             = 'Disa';
  68.     kMoreDisasm         = 'MDis';
  69.  
  70.     kHelpRequest        = '?';
  71.     kDontKnow            = ' Huh? ';
  72.  
  73.     kReserve            = 500;                            { Heap space reserved for the debugger's
  74.                                                          use. Too much?, Too little? }
  75.     kRecent             = 63;                            { must be a power of 2 minus 1 }
  76.  
  77.     { 68000 exception numbers that we intercept }
  78.     exBusError            = 2 * sizeof(Longint);
  79.     exAddressError        = 3 * sizeof(Longint);
  80.     exIllegalInst        = 4 * sizeof(Longint);
  81.     exZeroDivide        = 5 * sizeof(Longint);
  82.     exCheck             = 6 * sizeof(Longint);
  83.     exOverflow            = 7 * sizeof(Longint);
  84.     exLineF             = 11 * sizeof(Longint);
  85.  
  86. TYPE
  87.  
  88.     { Types for the Integrated environment interception calls }
  89.     IEFilePath            = STRING;
  90.     IEFilePathPtr        = ^IEFilePath;
  91.     IEFRefNum            = Longint;
  92.  
  93.  
  94.     {---}
  95.     WhyInDebugger        = (tBegin, tEnd, tExit, tBeginEndPair, { the rest always stop }
  96.                            tProgBreak, tSysError, tVBL, tReadLn);
  97.     ProcPtrPtr            = ^ProcPtr;
  98.  
  99.     HexAddress            = STRING[16];                    { Usually 8-9 chars. Sometimes a _small_
  100.                                                          string constant though. }
  101.  
  102.     QElemWithA5         = RECORD
  103.         OldA5:                Longint;                    { A place to store the old value of A5 since
  104.                                                          when debugging the compiler trashes the
  105.                                                          value of A0 for any locals in the VBL task
  106.                                                          thus makeing the pointer to the
  107.                                                          paramblockrec unavailable }
  108.         A5:                 Longint;                    { The value of A5 will be stored here to be
  109.                                                          available at VBL time }
  110.         q:                    QElem;                        { vbl queue element for changing the cursor}
  111.         END;
  112.  
  113.     VBLInfoPtr            = ^VBLInfo;
  114.     VBLInfo             = RECORD
  115.         aQElemWithA5:        QElemWithA5;                { vbl queue element for changing the cursor
  116.                                                          }
  117.         ch:                 CHAR;                        { character to represent the flag to the
  118.                                                          user with }
  119.         actionProc:         ProcPtr;                    { Pointer to a Proc that takes a boolean. If
  120.                                                          action is required when setting flag }
  121.         desc:                StringHandle;                { a description of the flag's function }
  122.         END;
  123.  
  124.     DebugFEntry         = RECORD
  125.         addr:                BooleanPtr;                 { Pointer to the actual boolean used for the
  126.                                                          flag }
  127.         ch:                 CHAR;                        { character to represent the flag to the
  128.                                                          user with }
  129.         actionProc:         ProcPtr;                    { Pointer to a Proc that takes a boolean. If
  130.                                                          action is required when setting flag }
  131.         desc:                StringHandle;                { a description of the flag's function }
  132.         END;
  133.  
  134.     DebugSEntry         = RECORD
  135.         addr:                Ptr;
  136.         actionProc:         ProcPtr;                    { Pointer to a Function that returns a Ptr.
  137.                                                          If action is required to get addr (pass
  138.                                                          nil for addr) }
  139.         sym:                MAName;
  140.         END;
  141.  
  142.     RecentPC            = RECORD
  143.         thePC:                Longint;
  144.         theWhyInDebugger:    WhyInDebugger;
  145.         END;
  146.  
  147.     HideType            = (PartialHide, FullHide);
  148.  
  149. VAR
  150. {$Push} {$J+}
  151.     pUDebugInitialized: BOOLEAN;
  152.     pCanEnterDebugger:    BOOLEAN;
  153.     pFileName:            Str255;                         { Name of file to intercept for IO }
  154.  
  155. {$Pop}
  156.  
  157.     pDisciplineMethodCalls: BOOLEAN;
  158.  
  159.     pVBLInfo:            VBLInfo;
  160.  
  161.     pTraceToggle, pTraceEnabled: BOOLEAN;
  162.     pBreakCount:        INTEGER;                        { current number of breakpoints set }
  163.     pBreakClass, pBreakProc: ARRAY [1..10] OF MAName;
  164.     pStackSpace:        Longint;                        { current total stack space; set in %_BP }
  165.     pProcStack:         Longint;                        { current stack space for just last
  166.                                                          procedure to do a %_BP }
  167.     pBreakStack:        Longint;
  168.     pStepOverStackSize: Longint;                        { when stepping the stack to break on if
  169.                                                          same or less }
  170.     pBrProcStack:        Longint;
  171.     pReserve:            Handle;
  172.  
  173.     pInterceptExceptionVectors: BOOLEAN;                { whether to intercept the 68xxx lo-memory
  174.                                                          exception vectors }
  175.     pOldexBusError, pOldexAddressError, pOldexIllegalInst, pOldexZeroDivide, pOldexCheck,
  176.     pOldexOverflow, pOldexLineF: ProcPtr;                { The old exception vectors }
  177.  
  178.     pSysErrPatch:        TrapPatch;
  179.  
  180.     pMoreMem:            Longint;                        {-1 if no more to see; 0 if more stack trace
  181.                                                          possible, else more memory dump}
  182.     pRecentPC:            ARRAY [0..kRecent] OF RecentPC; { PC ring buffer }
  183.     pRecentIndex:        INTEGER;
  184.  
  185.     pMasters:            INTEGER;                        { # available master pointers found by
  186.                                                          latest %_BP or %_EP }
  187.     pEnterProc:         Ptr;
  188.     pSymbolProc:        Ptr;
  189.  
  190.     pFlagTable:         TDynamicArray;                    { list of DebugFEntry records }
  191.     pSymTable:            TDynamicArray;                    { list of symbol table records }
  192.  
  193.     pPermFlag:            BOOLEAN;
  194.  
  195.     pTP2PerfGlobals:    TP2PerfGlobals;                 { Pointer to performance globals record
  196.                                                          Non-nil if tools are inited }
  197.  
  198.     fCaptureProc:        ProcPtr;                        { procedure for capturing output; set it
  199.                                                          with DebugCapture }
  200.  
  201.     pFullyHiddenFromMacApp: BOOLEAN;                    { Are we stopped in the read loop }
  202.     pQHdr:                QHdr;                            { Saved Event Queue Header }
  203.     pQSize:             INTEGER;                        { number of events }
  204.  
  205.     discardStr:         MAName;                         { a string that is used as a placeholder in
  206.                                                          any calls where rqd but the result is not
  207.                                                          rqd. Helps to reduce stack requirements }
  208.  
  209.     { the following were locals to MADebuggerMainEntry but… since the debugger is not re-entrant (for now) they can be
  210.     globals and thus available to the procedures that were nested in MADebuggerMainEntry but are no longer.
  211.     Also we knock off about 2k of stack requirements. }
  212.     gWhyInDebugger:     WhyInDebugger;
  213.     pLink:                Longint;
  214.     ppc:                Longint;
  215.     aClassName:         MAName;
  216.     aProcName:            MAName;
  217.     aMiscName:            MAName;
  218.     asDecimal, asHex:    Longint;
  219.     pAtBreak:            BOOLEAN;
  220.     callerFrame:        Longint;
  221.     ch:                 CHAR;
  222.     className:            MAName;
  223.     itsFrame:            Longint;
  224.     nextFrame:            Longint;
  225.     nextLevel:            INTEGER;
  226. {$Ifc qPerform}
  227.     oldState:            BOOLEAN;                        { State of Performance monitoring when
  228.                                                          enterproc called and the state to which
  229.                                                          monitering will return. Performance
  230.                                                          monitering toggle changes this value }
  231. {$Endc}
  232.     pNextPC:            Longint;
  233.     prevFrame:            Longint;
  234.     procName:            MAName;
  235.     rcvrClass:            MAName;
  236.     rcvrHandle:         HexAddress;
  237.     receiver:            TObject;
  238.     segNum:             INTEGER;
  239.     stkBreak:            BOOLEAN;
  240.     stepBreak:            BOOLEAN;
  241.     str:                MAName;
  242.     pStoppedInDebugger: BOOLEAN;
  243.     lastCH:             CHAR;
  244.  
  245.     theDebuggerAddress: AEAddressDesc;
  246.     pHasDebuggerAddress: BOOLEAN;
  247. {--------------------------------------------------------------------------------------------------}
  248. {$Ifc qPerform}
  249. {$S MADebugger}
  250.  
  251. FUNCTION DebugPerfMonitor(turnOn: BOOLEAN): BOOLEAN;
  252. { Turns performance tracing on and off if installed. }
  253.  
  254.     BEGIN
  255.     IF (pTP2PerfGlobals <> NIL) & pUDebugInitialized THEN
  256.         DebugPerfMonitor := PerfControl(pTP2PerfGlobals, turnOn)
  257.     ELSE
  258.         DebugPerfMonitor := FALSE;
  259.     END;
  260. {$Endc}
  261.  
  262. {$IFC qDebug}
  263. {--------------------------------------------------------------------------------------------------}
  264. FUNCTION _addDevHandler(slot, dvName, dvFAccess, dvClose, dvRead, dvWrite,
  265.                         dvIoctl: Longint): Longint;
  266.     C; EXTERNAL;
  267.  
  268. {--------------------------------------------------------------------------------------------------}
  269. { The following are assembler routines in UDebug.a }
  270.  
  271. PROCEDURE XDebugSysError;
  272.     EXTERNAL;
  273. { PROCEDURE XDebugNMI;    EXTERNAL; }
  274.  
  275. PROCEDURE XDebugBusError;
  276.     EXTERNAL;
  277.  
  278. PROCEDURE XDebugAddrError;
  279.     EXTERNAL;
  280.  
  281. PROCEDURE XDebugIllInst;
  282.     EXTERNAL;
  283.  
  284. PROCEDURE XDebugZeroDiv;
  285.     EXTERNAL;
  286.  
  287. PROCEDURE XDebugCheck;
  288.     EXTERNAL;
  289.  
  290. PROCEDURE XDebugOverflow;
  291.     EXTERNAL;
  292.  
  293. PROCEDURE XDebugLineF;
  294.     EXTERNAL;
  295.  
  296. {--------------------------------------------------------------------------------------------------}
  297. PROCEDURE VBLInstall;
  298.     FORWARD;
  299.  
  300. PROCEDURE VBLRemove;
  301.     FORWARD;
  302.  
  303. PROCEDURE NubWaitNextEvent;
  304.     FORWARD;
  305.  
  306. FUNCTION DebuggerDispatch(message, reply: AppleEvent; info: Longint): OSErr;
  307.     FORWARD;
  308.  
  309. {--------------------------------------------------------------------------------------------------}
  310. { The following are assembler routines in UDebug.cp }
  311.  
  312. FUNCTION IsFrontProcess: Boolean;
  313.     EXTERNAL;
  314.  
  315. FUNCTION DevFAccess(fName: UNIV IEFilePathPtr; opCode: Longint; arg: UNIV Longint): Longint;
  316.     C; EXTERNAL;
  317.  
  318. FUNCTION DevClose(fdesc: IEFRefNum): Longint;
  319.     C; EXTERNAL;
  320.  
  321. FUNCTION DevRead(fdesc: IEFRefNum; bufp: UNIV Longint; count: Longint): Longint;
  322.     C; EXTERNAL;
  323.  
  324. FUNCTION DevWrite(fdesc: IEFRefNum; bufp: UNIV Longint; count: Longint): Longint;
  325.     C; EXTERNAL;
  326.  
  327. FUNCTION DevIoctl(fdesc: IEFRefNum; request: Longint; arg: UNIV Longint): Longint;
  328.     C; EXTERNAL;
  329.  
  330. FUNCTION SetGetProc(theGetProc: ProcPtr): ProcPtr;
  331.     EXTERNAL;
  332.  
  333. FUNCTION SetPutProc(thePutProc: ProcPtr): ProcPtr;
  334.     EXTERNAL;
  335.  
  336. {--------------------------------------------------------------------------------------------------}
  337.  
  338. FUNCTION CallSymActionProc(actionProc: ProcPtr): Handle;
  339.     INLINE $205F, $4E90;
  340. {  MOVE.L  (A7)+,A0
  341. JSR (A0)
  342. }
  343.  
  344. FUNCTION CallSymbolLookup(VAR sym: Str255; lookerUpper: Ptr): Longint;
  345.     INLINE $205F, $4E90;
  346. {  MOVE.L  (A7)+,A0
  347. JSR (A0)
  348. }
  349.  
  350. PROCEDURE CallInspector(obj: TObject; inspector: Ptr);
  351.     INLINE $205F, $4E90;
  352. { MOVE.L (A7)+,A0
  353. JSR (A0)
  354. }
  355.  
  356. FUNCTION CallFlagActionProc(OnOrOff: BOOLEAN; actionProc: ProcPtr): BOOLEAN;
  357.     INLINE $205F, $4E90;
  358. {  MOVE.L  (A7)+,A0
  359. JSR (A0)
  360. }
  361.  
  362. PROCEDURE CallEnter(entering: BOOLEAN; proc: Ptr);
  363.     INLINE $205F, $4E90;
  364. {  MOVE.L  (A7)+,A0
  365. JSR (A0)
  366. }
  367.  
  368. PROCEDURE CallCapture(textBuf: Ptr; byteCount: Longint; captureProc: ProcPtr);
  369.     INLINE $205F, $4E90;
  370. { MOVEA.L (A7)+,A0
  371. JSR (A0)
  372. }
  373.  
  374. {--------------------------------------------------------------------------------------------------}
  375. {$S MADebugger}
  376.  
  377. FUNCTION NubGetDebuggerAddress: AEAddressDesc;
  378.  
  379.     VAR
  380.         theTargetID:        TargetID;
  381.         theLoc:             LocationNameRec;
  382.         thePortInfo:        PortInfoRec;
  383.         theErr:             OSErr;
  384.  
  385.         theMessage:         AEDesc;
  386.         theReply:            AEDesc;
  387.  
  388.     BEGIN
  389.     { Get a debugger address if necessary }
  390.     IF NOT pHasDebuggerAddress THEN
  391.         BEGIN
  392.         theErr := PPCBrowser('Please find the debugger', '', FALSE, theLoc, thePortInfo, NIL, '');
  393.         IF theErr = NoErr THEN
  394.             BEGIN
  395.             theTargetID.location := theLoc;
  396.             theTargetID.name := thePortInfo.name;
  397.  
  398.             FailOSErr(AECreateDesc(typeTargetID, @theTargetID, sizeof(TargetID),
  399.                       theDebuggerAddress));
  400.             pHasDebuggerAddress := TRUE;
  401.             NubGetDebuggerAddress := theDebuggerAddress;
  402.             END
  403.         ELSE
  404.             BEGIN
  405.             DebugStr('couldn''t find MacApp debugger');
  406.             ExitToShell;                                { Cancelled??? }
  407.             END;
  408.         END
  409.     ELSE
  410.         NubGetDebuggerAddress := theDebuggerAddress;
  411.  
  412.     END;
  413.  
  414. {--------------------------------------------------------------------------------------------------}
  415. {$S MADebugger}
  416.  
  417. FUNCTION YouAreWarned: BOOLEAN;
  418. { Returns true if the super secret power keys are held down.
  419. Used to indicate to the debugger that the programmer wants to flirt with _DANGER_!
  420. If you do this then you're _ON_YOUR_OWN. }
  421.  
  422.     VAR
  423.         aKeyMap:            KeyMap;
  424.         oldState:            INTEGER;
  425.  
  426.     BEGIN
  427.     oldState := IntegerPtr(JournalFlag)^;
  428.     IntegerPtr(JournalFlag)^ := 0;                        { turn off journaling }
  429.     GetKeys(aKeyMap);
  430.     IntegerPtr(JournalFlag)^ := oldState;
  431.     IF aKeyMap[$3B] THEN                                { Control key }
  432.         YouAreWarned := TRUE
  433.     ELSE
  434.         YouAreWarned := FALSE;
  435.     END;
  436.  
  437. {--------------------------------------------------------------------------------------------------}
  438. {$S MADebugger}
  439.  
  440. PROCEDURE SaveEventQueue(save: BOOLEAN);
  441.  
  442.     CONST
  443.         kLMEvtBufCnt        = $154;
  444.  
  445.     BEGIN
  446.     IF save THEN
  447.         BEGIN
  448.         { Save the existing event queue }
  449.         pQHdr := GetEvQHdr^;
  450.         WITH GetEvQHdr^ DO
  451.             BEGIN
  452.             qFlags := 0;
  453.             qHead := NIL;
  454.             qTail := NIL;
  455.             END;
  456.         pQSize := IntegerPtr(kLMEvtBufCnt)^;
  457.         END
  458.     ELSE
  459.         BEGIN
  460.         { Restore the event queue }
  461.         {    FlushEvents(everyEvent, 0); }
  462.         GetEvQHdr^ := pQHdr;
  463.         IntegerPtr(kLMEvtBufCnt)^ := pQSize;
  464.         END;
  465.     END;
  466.  
  467. {--------------------------------------------------------------------------------------------------}
  468. {$S MADebugger}
  469.  
  470. PROCEDURE WithHideFromMacAppDo(PROCEDURE WhatToDo; itsHideType: HideType);
  471. {
  472. Intended for doit behind MacApp's back stuff.
  473. Fullhide indicates whether to give enough support to fully stop in the debugger
  474. }
  475.  
  476.     VAR
  477.         oldpCanEnterDebugger: BOOLEAN;
  478.  
  479.         oldpFullyHiddenFromMacApp: BOOLEAN;
  480.         OldA5: Longint;
  481.         oldResLoad: BOOLEAN;
  482.         oldResFile: INTEGER;
  483.         fi: FailInfo;
  484.  
  485.     PROCEDURE UnloadActivateEvents;
  486.     { Activate events are manufactured by the window manager
  487.     Thus they need to be preserved. The activate event if any
  488.     is retrieved then the procedure recursed to get any more.  Then
  489.     the events are reposted on the application event queue. }
  490.  
  491.         VAR
  492.             theEvent: EventRecord;
  493.             aEvQElPtr: EvQElPtr;
  494.  
  495.         BEGIN
  496.         IF GetNextEvent(activMask, theEvent) THEN
  497.             BEGIN
  498.             UnloadActivateEvents; { recurse to get more }
  499.             WITH theEvent DO
  500.                 BEGIN
  501.                 IF (PPostEvent(activateEvt, message, aEvQElPtr)) = NoErr THEN
  502.                     aEvQElPtr^.evtQmodifiers := modifiers;
  503.                 END;
  504.             END;
  505.         END;
  506.  
  507.     PROCEDURE HdlFailure(error: INTEGER; message: Longint);
  508.  
  509.         BEGIN
  510.         pCanEnterDebugger := oldpCanEnterDebugger;
  511.         pFullyHiddenFromMacApp := FALSE;
  512.  
  513.         IF MAUseResFile(oldResFile) = 0 THEN;
  514.         SetResLoad(oldResLoad);
  515.         OldA5 := SetA5(OldA5);
  516.         {###        SaveEventQueue(FALSE);}
  517.  
  518.         CallEnter(FALSE, pEnterProc);
  519.         pCanEnterDebugger := TRUE;
  520.         END;
  521.  
  522.     BEGIN
  523.     oldpFullyHiddenFromMacApp := pFullyHiddenFromMacApp;
  524.     oldpCanEnterDebugger := pCanEnterDebugger;
  525.  
  526.     OldA5 := SetCurrentA5;
  527.     oldResLoad := GetResLoad;
  528.     SetResLoad(TRUE);
  529.     oldResFile := MAUseResFile(gApplicationRefNum);
  530. {###        UnloadActivateEvents;
  531.         SaveEventQueue(true);}
  532.  
  533.     IF NOT oldpFullyHiddenFromMacApp THEN
  534.         CASE itsHideType OF
  535.             PartialHide:
  536.                 pCanEnterDebugger := FALSE;
  537.  
  538.             FullHide:
  539.                 BEGIN
  540.                 pCanEnterDebugger := FALSE;
  541.                 pFullyHiddenFromMacApp := TRUE;
  542.  
  543.                 END; { FullHide }
  544.         END; { CASE }
  545.  
  546.     CatchFailures(fi, HdlFailure);
  547.  
  548.     WhatToDo;
  549.  
  550.     Success(fi);
  551.  
  552.     pCanEnterDebugger := oldpCanEnterDebugger;
  553.     pFullyHiddenFromMacApp := FALSE;
  554.  
  555.     IF MAUseResFile(oldResFile) = 0 THEN;
  556.     SetResLoad(oldResLoad);
  557.     OldA5 := SetA5(OldA5);
  558.     {###        SaveEventQueue(FALSE);}
  559.     END;
  560.  
  561. {--------------------------------------------------------------------------------------------------}
  562. {$S MADebugger}
  563.  
  564. FUNCTION DebugReadCh: CHAR;
  565.  
  566.     VAR
  567.         C:                    CHAR;
  568.         theMessage:         AEDesc;
  569.         theReply:            AEDesc;
  570.  
  571.     BEGIN
  572.     { guarantee that user can see prompts }
  573.     PLflush(output);
  574.  
  575.     { Create the basic message to send }
  576.     FailOSErr(AECreateAppleEvent('MADB', kRequestUserInput, NubGetDebuggerAddress,
  577.                                  kAutoGenerateReturnID, kAnyTransactionID, theMessage));
  578.  
  579.     { Send it off, and don't worry about a reply or receipt }
  580.     FailOSErr(AESend(theMessage, theReply, kAENoReply, kAENormalPriority, 1000, NIL, NIL));
  581.  
  582.     FailOSErr(AEDisposeDesc(theMessage));
  583.  
  584.     WHILE (lastCH = chr(0)) DO
  585.         NubWaitNextEvent;
  586.  
  587.     DebugReadCh := lastCH;
  588.     lastCH := chr(0);
  589.     END;
  590.  
  591. {$EndC}
  592. {$IFC qDebug}
  593. {--------------------------------------------------------------------------------------------------}
  594. {$S MADebugger}
  595.  
  596. FUNCTION DebugReadLn(buffer: Ptr; byteCount: Longint): Longint;
  597.  
  598.     TYPE
  599.         PA1000                = PACKED ARRAY [0..999] OF CHAR;
  600.         StrPtr                = ^PA1000;
  601.  
  602.     VAR
  603.         ch:                 CHAR;
  604.         len:                INTEGER;
  605.  
  606.     PROCEDURE WhatToDo;
  607.  
  608.         BEGIN
  609.         len := 0;
  610.  
  611.         REPEAT
  612.             ch := DebugReadCh;
  613.  
  614.             CASE ch OF
  615.                 chBackspace:
  616.                     IF len > 0 THEN
  617.                         BEGIN
  618.                         Write(ch);
  619.                         len := len - 1;
  620.                         StrPtr(buffer)^[len] := ' ';
  621.                         END;
  622.                 OTHERWISE
  623.                     BEGIN
  624.                     Write(ch);
  625.                     StrPtr(buffer)^[len] := ch;
  626.                     len := len + 1;
  627.                     END
  628.             END;
  629.         UNTIL (ch = chReturn) | (len = byteCount);
  630.  
  631.         DebugReadLn := len;
  632.         END;
  633.  
  634.     BEGIN
  635.     IF FALSE & NOT pFullyHiddenFromMacApp THEN
  636.         BEGIN
  637.         gWhyInDebugger := tReadLn;
  638.         END;
  639.  
  640.     WithHideFromMacAppDo(WhatToDo, FullHide);
  641.     END;
  642.  
  643. {--------------------------------------------------------------------------------------------------}
  644. {$S MADebugger}
  645.  
  646. PROCEDURE InstallExceptionHandlers(install: BOOLEAN);
  647.  
  648.     BEGIN
  649.  
  650.     IF install THEN
  651.         BEGIN
  652.         { Intercept 68000 exceptions }
  653.         IF pInterceptExceptionVectors THEN
  654.             BEGIN
  655.             pOldexBusError := ProcPtrPtr(exBusError)^;
  656.             ProcPtrPtr(exBusError)^ := @XDebugBusError;
  657.  
  658.             pOldexAddressError := ProcPtrPtr(exAddressError)^;
  659.             ProcPtrPtr(exAddressError)^ := @XDebugAddrError;
  660.  
  661.             pOldexIllegalInst := ProcPtrPtr(exIllegalInst)^;
  662.             ProcPtrPtr(exIllegalInst)^ := @XDebugIllInst;
  663.  
  664.             pOldexZeroDivide := ProcPtrPtr(exZeroDivide)^;
  665.             ProcPtrPtr(exZeroDivide)^ := @XDebugZeroDiv;
  666.  
  667.             pOldexCheck := ProcPtrPtr(exCheck)^;
  668.             ProcPtrPtr(exCheck)^ := @XDebugCheck;
  669.  
  670.             pOldexOverflow := ProcPtrPtr(exOverflow)^;
  671.             ProcPtrPtr(exOverflow)^ := @XDebugOverflow;
  672.  
  673.             pOldexLineF := ProcPtrPtr(exLineF)^;
  674.             ProcPtrPtr(exLineF)^ := @XDebugLineF;
  675.             END;
  676.  
  677.         { Intercept SysError calls }
  678.         FailOSErr(PatchTrap(pSysErrPatch, _SysError, @XDebugSysError));
  679.         END
  680.     ELSE
  681.         BEGIN
  682.         { UN-Intercept 68000 exceptions }
  683.         IF pInterceptExceptionVectors THEN
  684.             BEGIN
  685.             IF ProcPtrPtr(exBusError)^ = @XDebugBusError THEN
  686.                 ProcPtrPtr(exBusError)^ := pOldexBusError;
  687.  
  688.             IF ProcPtrPtr(exAddressError)^ = @XDebugAddrError THEN
  689.                 ProcPtrPtr(exAddressError)^ := pOldexAddressError;
  690.  
  691.             IF ProcPtrPtr(exIllegalInst)^ = @XDebugIllInst THEN
  692.                 ProcPtrPtr(exIllegalInst)^ := pOldexIllegalInst;
  693.  
  694.             IF ProcPtrPtr(exZeroDivide)^ = @XDebugZeroDiv THEN
  695.                 ProcPtrPtr(exZeroDivide)^ := pOldexZeroDivide;
  696.  
  697.             IF ProcPtrPtr(exCheck)^ = @XDebugCheck THEN
  698.                 ProcPtrPtr(exCheck)^ := pOldexCheck;
  699.  
  700.             IF ProcPtrPtr(exOverflow)^ = @XDebugOverflow THEN
  701.                 ProcPtrPtr(exOverflow)^ := pOldexOverflow;
  702.  
  703.             IF ProcPtrPtr(exLineF)^ = @XDebugLineF THEN
  704.                 ProcPtrPtr(exLineF)^ := pOldexLineF;
  705.             END;
  706.  
  707.         { UN-Intercept SysError calls }
  708.         UnpatchTrap(pSysErrPatch);
  709.         END;
  710.     END;
  711.  
  712. {--------------------------------------------------------------------------------------------------}
  713. {$S MADebugger}
  714.  
  715. PROCEDURE JTOffProc(A5JTOffset: UNIV INTEGER; VAR s: UNIV Str255 {DisAsmStr80});
  716.  
  717.     CONST
  718.         kUnloaded            = $3F3C;
  719.  
  720.     VAR
  721.         aName:                MAName;
  722.         pc:                 Longint;
  723.  
  724.     BEGIN
  725.     pc := Longint(GetA5) + A5JTOffset;
  726.     IF IntegerPtr(pc)^ <> kUnloaded THEN
  727.         BEGIN
  728.         GetMethodName(ord(@pc), aName);
  729.         s := aName;
  730.         END
  731.     ELSE
  732.         s := '';
  733.     END;
  734.  
  735. {$EndC}
  736.  
  737. {--------------------------------------------------------------------------------------------------}
  738. {$S MADebugger}
  739.  
  740. FUNCTION IsUserBreak: BOOLEAN;
  741.  
  742.     VAR
  743.         aKeyMap:            KeyMap;
  744.         oldState:            INTEGER;
  745.  
  746.     BEGIN
  747.     oldState := IntegerPtr(JournalFlag)^;
  748.     IntegerPtr(JournalFlag)^ := 0;                        { turn off journaling }
  749.     GetKeys(aKeyMap);
  750.     IntegerPtr(JournalFlag)^ := oldState;
  751.     IsUserBreak := aKeyMap[55] & aKeyMap[56] & aKeyMap[58] & (NOT qDebug | pUDebugInitialized);
  752.     END;
  753.  
  754. {$IFC qDebug}
  755. {--------------------------------------------------------------------------------------------------}
  756. {$S MADebugger}
  757.  
  758. PROCEDURE stdHelpProc;
  759.  
  760.     BEGIN
  761.     WriteLn;
  762.     WriteLn('Reply with one of the letters in the brackets');
  763.     WriteLn;
  764.     END;
  765.  
  766. {--------------------------------------------------------------------------------------------------}
  767. {$S MADebugger}
  768.  
  769. FUNCTION GetPromptedChar(prompt: StringPtr; validChars: StringPtr; PROCEDURE helpProc): CHAR;
  770.  
  771.     VAR
  772.         ch:                 CHAR;
  773.         done:                BOOLEAN;
  774.         index:                INTEGER;
  775.  
  776.     PROCEDURE WriteThePrompt;
  777.  
  778.         BEGIN
  779.         Write(prompt^); Write(' ['); Write(validChars^); Write(kHelpRequest);
  780.         Write(']: ');
  781.         END;
  782.  
  783.     BEGIN
  784.     WriteThePrompt;
  785.     REPEAT
  786.         ch := UprChar(DebugReadCh);
  787.         CASE ch OF
  788.             kHelpRequest, chHelp:
  789.                 BEGIN
  790.                 helpProc;
  791.                 WriteThePrompt;
  792.                 done := FALSE
  793.                 END;
  794.             chReturn:
  795.                 BEGIN
  796.                 WriteLn;
  797.                 done := TRUE;
  798.                 END;
  799.             OTHERWISE
  800.                 BEGIN
  801.                 FOR index := 1 TO length(validChars^) DO
  802.                     IF ch = UprChar(validChars^[index]) THEN
  803.                         BEGIN
  804.                         WriteLn(ch);
  805.                         done := TRUE;
  806.                         LEAVE;
  807.                         END;
  808.                 IF index > length(validChars^) THEN
  809. {###SRF                    gApplication.Beep(30);    };            { 1/2 second }
  810.                 END;
  811.         END;
  812.     UNTIL done;
  813.     GetPromptedChar := ch;
  814.     END;
  815.  
  816. {--------------------------------------------------------------------------------------------------}
  817. {$S MADebugger}
  818.  
  819. FUNCTION GetPromptedString(prompt: StringPtr; PROCEDURE helpProc): Str255;
  820.  
  821.     VAR
  822.         returnStr:            Str255;
  823.         done:                BOOLEAN;
  824.  
  825.     BEGIN
  826.     Write(prompt^);
  827.     returnStr := '';
  828.     REPEAT
  829.         ch := DebugReadCh;
  830.         CASE ch OF
  831.             chHelp:
  832.                 BEGIN
  833.                 WriteLn;
  834.                 helpProc;
  835.                 Write(prompt^);
  836.                 done := FALSE
  837.                 END;
  838.             chBackspace:
  839.                 BEGIN
  840.                 IF length(returnStr) > 0 THEN
  841.                     BEGIN
  842.                     Write(ch);
  843.                     returnStr[0] := chr(max(length(returnStr) - 1, 0));
  844.                     END;
  845.                 done := FALSE
  846.                 END;
  847.             chReturn:
  848.                 BEGIN
  849.                 Write(ch);
  850.                 IF returnStr = kHelpRequest THEN
  851.                     BEGIN
  852.                     returnStr := '';
  853.                     helpProc;
  854.                     Write(prompt^);
  855.                     done := FALSE
  856.                     END
  857.                 ELSE
  858.                     done := TRUE;
  859.                 END;
  860.             OTHERWISE
  861.                 BEGIN
  862.                 Write(ch);
  863.                 returnStr := concat(returnStr, ch);
  864.                 done := FALSE;
  865.                 END;
  866.         END;
  867.     UNTIL done;
  868.     GetPromptedString := returnStr;
  869.     END;
  870.  
  871. {--------------------------------------------------------------------------------------------------}
  872. {$S MADebugger}
  873.  
  874. FUNCTION GetFreeMastersCount: Longint;
  875.  
  876.     VAR
  877.         zone:                THZ;
  878.         pL:                 LongIntPtr;
  879.         mpCnt:                Longint;
  880.  
  881.     BEGIN
  882.     zone := ApplicZone;
  883.     pL := LongIntPtr(zone^.hFstFree);
  884.     mpCnt := 0;
  885.     WHILE pL <> NIL DO
  886.         BEGIN
  887.         mpCnt := mpCnt + 1;
  888.         pL := LongIntPtr(pL^);
  889.         END;
  890.     GetFreeMastersCount := mpCnt;
  891.     END;
  892.  
  893. {--------------------------------------------------------------------------------------------------}
  894. {$S MADebugger}
  895.  
  896. PROCEDURE CheckFreeMasters;
  897.  
  898.     VAR
  899.         mp:                 Longint;
  900.  
  901.     BEGIN
  902.     IF pMasters > 0 THEN                                { we computed # masters before }
  903.         BEGIN
  904.         mp := GetFreeMastersCount;
  905.         IF pMasters <> mp THEN
  906.             BEGIN
  907.             WriteLn('pMasters: ', pMasters, '  current masters: ', mp);
  908.             IF gMemMgtBreak THEN
  909.                 gSingleStep := TRUE;
  910.             END;
  911.         END;
  912.  
  913.     pMasters := GetFreeMastersCount
  914.     END;
  915.  
  916. {--------------------------------------------------------------------------------------------------}
  917. {$S MADebugger}
  918.  
  919. PROCEDURE DebugWriteLnHook(textBuf: Ptr; byteCount: Longint);
  920.  
  921.     PROCEDURE WhatToDo;
  922.  
  923.         VAR
  924.             theMessage:         AEDesc;
  925.             theReply:            AEDesc;
  926.  
  927.         BEGIN
  928.         IF fCaptureProc <> NIL THEN
  929.             CallCapture(textBuf, byteCount, fCaptureProc);
  930.  
  931.         { Create the basic message to send }
  932.         FailOSErr(AECreateAppleEvent('MADB', kReadableText, NubGetDebuggerAddress,
  933.                                      kAutoGenerateReturnID, kAnyTransactionID, theMessage));
  934.  
  935.         { Put the pointer data in as direct parameter… }
  936.         FailOSErr(AEPutParamPtr(theMessage, keyDirectObject, 'data', textBuf, byteCount));
  937.  
  938.         { Send it off, and don't worry about a reply or receipt }
  939.         FailOSErr(AESend(theMessage, theReply, kAENoReply, kAENormalPriority, 1000, NIL, NIL));
  940.  
  941.         FailOSErr(AEDisposeDesc(theMessage));
  942.         END;
  943.  
  944.     BEGIN
  945.     WithHideFromMacAppDo(WhatToDo, PartialHide);
  946.     END;
  947.  
  948. {--------------------------------------------------------------------------------------------------}
  949. {$S MADebugger}
  950.  
  951. PROCEDURE InstallWriteLnHook;
  952.  
  953.     CONST
  954.         kConsoleName        = 'dev:console';
  955.         _CODEV                = 1;                        { console device number }
  956.  
  957.     VAR
  958.         slot:                Longint;
  959.         oldProc:            ProcPtr;
  960.  
  961.     BEGIN
  962.     pFileName := kConsoleName;
  963.     slot := _addDevHandler(_CODEV, 0, ord(@DevFAccess), ord(@DevClose), ord(@DevRead),
  964.                            ord(@DevWrite), ord(@DevIoctl));
  965.     PLsetvbuf(output, NIL, _IOLBF, 128);
  966.     oldProc := SetGetProc(@DebugReadLn);
  967.     oldProc := SetPutProc(@DebugWriteLnHook);
  968.     END;
  969.  
  970. {--------------------------------------------------------------------------------------------------}
  971. {$S MAInit}
  972.  
  973. PROCEDURE InitUDebug(segTable, nonRes: Handle; enterProc, symbolProc: Ptr);
  974. { essential initialization (segTable, nonRes left in for compatibility (2.0) }
  975.  
  976.     CONST
  977.         kDebugHeight        = 100;
  978.         kVMargin            = 4;
  979.         kHMargin            = 4;
  980.  
  981.     TYPE
  982.         dbugParams            = RECORD                    { Format of 'dbug' resource }
  983.             boundsRect:         Rect;                    { Rect of debugging window }
  984.             fontNumber:         INTEGER;                { Font rsrc ID }
  985.             fontSize:            INTEGER;                { Font size }
  986.             numLines:            INTEGER;                { Number of lines }
  987.             lineWidth:            INTEGER;                { Line width }
  988.             openInitially:        BOOLEAN;                { Open Initially }
  989.             title:                Str255;                 { Actually, variable length }
  990.             END;
  991.         dbugParamsPtr        = ^dbugParams;
  992.         dbugParamsHandle    = ^dbugParamsPtr;
  993.  
  994.     VAR
  995.         wasTrcEnable:        BOOLEAN;
  996.         dParams:            Handle;
  997.  
  998.         addr:                Longint;
  999.         i:                    INTEGER;
  1000.         err:                OSErr;
  1001.         vhs:                VHSelect;
  1002.         zoomedOutSize:        Point;
  1003.         aDebugParams:        dbugParams;
  1004.         aTextStyle:         TextStyle;
  1005.         Errs:                Handle;
  1006.  
  1007.     BEGIN
  1008.     pFullyHiddenFromMacApp := FALSE;
  1009.     pCanEnterDebugger := FALSE;
  1010.     pDisciplineMethodCalls := TRUE;                     { matches default in uobject }
  1011.     pHasDebuggerAddress := FALSE;
  1012.  
  1013.     pInterceptExceptionVectors := TRUE;
  1014.  
  1015.     pTP2PerfGlobals := NIL;
  1016.  
  1017.     lastCH := chr(0);
  1018.  
  1019.     pTraceToggle := FALSE;
  1020.     gSingleStep := FALSE;
  1021.     pBreakCount := 0;
  1022.     pTraceEnabled := FALSE;
  1023.     gTracing := FALSE;
  1024.     gReportNext := FALSE;
  1025.     gReportInfo := '';
  1026.     gReportTime := FALSE;
  1027.  
  1028.     pMasters := - 1;
  1029.  
  1030.     New(pFlagTable);
  1031.     pFlagTable.IDynamicArray(0, sizeof(DebugFEntry));
  1032.  
  1033.     New(pSymTable);
  1034.     pSymTable.IDynamicArray(0, sizeof(DebugSEntry));
  1035.  
  1036.     gMaxStackDepth := - 1;
  1037.     pBreakStack := $7FFFFFFF;
  1038.     pStepOverStackSize := 0;
  1039.     pBrProcStack := $7FFFFFFF;
  1040.     gMaxLockedRsrc := 0;
  1041.  
  1042.     pEnterProc := enterProc;
  1043.     pSymbolProc := symbolProc;
  1044.  
  1045.     FOR i := 0 TO kRecent DO
  1046.         BEGIN
  1047.         pRecentPC[i].thePC := 0;
  1048.         pRecentPC[i].theWhyInDebugger := tSysError;
  1049.         END;
  1050.     pRecentIndex := 0;
  1051.  
  1052.     fCaptureProc := NIL;
  1053.     pReserve := NewPermHandle(kReserve);                { Reserve some space in case of SysErr }
  1054.     FailNil(pReserve);
  1055.  
  1056. {###SRF    InstallExceptionHandlers(TRUE);}
  1057.  
  1058. {$IFC IncludeDisassembler}
  1059.     { Init Ira's disassembler }
  1060.     InitLookup(NIL, @JTOffProc, @LookupTrapName, NIL, NIL);
  1061. {$EndC}
  1062.  
  1063.     VBLInstall;
  1064.  
  1065. {### need to use accessors now that these are fields
  1066.     DebugGlobalHandle(@pSavedState.fTarget, NIL, AtMAName('fTarget'));
  1067.     DebugGlobalHandle(@gDocList, NIL, AtMAName('gDocList'));
  1068.     DebugGlobalHandle(@gFreeWindowList, NIL, AtMAName('gFreeWindowList'));
  1069.     DebugGlobalHandle(@gApplication.fClipView, NIL, AtMAName('fClipView'));
  1070.     DebugGlobalHandle(@gApplication.fClipUndoView, NIL, AtMAName('fClipUndoView'));
  1071.  
  1072.     DebugGlobalHandle(@gPrintHandler, NIL, AtMAName('gPrintHandler'));
  1073.     DebugGlobalHandle(@gFocusedView, NIL, AtMAName('gFocusedView'));
  1074.  
  1075.     DebugGlobalHandle(NIL, @DebugGetLastCommand, AtMAName('GetLastCommand'));
  1076.     DebugGlobalHandle(NIL, @DebugGetActiveWindow, AtMAName('GetActiveWindow'));
  1077.     DebugGlobalHandle(NIL, @DebugGetActiveDocument, AtMAName('GetActiveDocument'));
  1078. }
  1079.  
  1080.     DebugFlag(@gIntenseDebugging, 'I', NIL, AtStr('Intense debugging'));
  1081.     DebugFlag(@gShowInvalidations, 'L', NIL, AtStr('Show Invalidations'));
  1082.     DebugFlag(@gShowCursorRegion, 'K', NIL, AtStr('Show Cursor Region'));
  1083.     DebugFlag(@gShowHelpRegion, 'H', NIL, AtStr('Show Help Region'));
  1084.     DebugFlag(@gShowSleepRegion, 'W', NIL, AtStr('Show Sleep Region'));
  1085.     DebugFlag(@gMemMgtBreak, 'B', NIL, AtStr('Memory management break'));
  1086.     DebugFlag(@gMastReport, 'M', NIL, AtStr('Report # masters'));
  1087.     DebugFlag(@gSegReport, 'S', NIL, AtStr('Report segment load'));
  1088.     DebugFlag(@gUnloadAllSegs, 'U', NIL, AtStr('Unload segments'));
  1089.     DebugFlag(@gExperimenting, 'X', NIL, AtStr('Experimenting'));
  1090.     DebugFlag(@gAskFailure, 'F', NIL, AtStr('Ask about failures'));
  1091.     DebugFlag(@gReportEvt, 'E', NIL, AtStr('Report events'));
  1092.     DebugFlag(@gAskAboutAlloc, 'A', NIL, AtStr('Ask about allocations'));
  1093.     DebugFlag(@gRsrcReport, 'R', NIL, AtStr('Report resource usage'));
  1094.     DebugFlag(@gReportMenuChoices, 'C', NIL, AtStr('Report menu commands'));
  1095.     DebugFlag(@gDebugPrinting, 'P', NIL, AtStr('Printing debug'));
  1096.     DebugFlag(@pDisciplineMethodCalls, 'D', @DisciplineMethodCalls,
  1097.               AtStr('Discipline method calls'));
  1098.     DebugFlag(@gAssumeFocused, 'V', NIL, AtStr('Do "AssumeFocused" preconditioning'));
  1099.  
  1100. {$IFC qExperimentalAndUnsupported}
  1101.     DebugFlag(@gEnableDoubleBuffering, 'G', NIL, AtStr('Enable double buffering of views'));
  1102. {$EndC}
  1103.  
  1104.     {### Move err strings out of nub }
  1105.     { Make sure the error strings are always available by loading them and but not
  1106.     letting them be purgeable }
  1107.     Errs := GetResource('STR#', 252);
  1108.     FailNILResource(Errs);
  1109.     HNoPurge(Errs);
  1110.  
  1111.     { take all debugger events }
  1112.     FailOSErr(AEInstallEventHandler('MADB', typeWildCard, @DebuggerDispatch, cNoCommand, FALSE));
  1113.  
  1114.     { LAST THING ON INIT: install the console interceptor }
  1115.     InstallWriteLnHook;
  1116.  
  1117.     pUDebugInitialized := TRUE;
  1118.     pCanEnterDebugger := TRUE;
  1119.  
  1120.     END;
  1121.  
  1122. {--------------------------------------------------------------------------------------------------}
  1123. {$S MADebugger}
  1124.  
  1125. PROCEDURE DebugTerminate;
  1126.  
  1127.     BEGIN
  1128.     IF pUDebugInitialized THEN
  1129.         BEGIN
  1130.         VBLRemove;
  1131.  
  1132. {$IFC qPerform}
  1133.         { Make sure the performance tools are shut down if they are initialized }
  1134.         IF pTP2PerfGlobals <> NIL THEN
  1135.             BEGIN
  1136.             TermPerf(pTP2PerfGlobals);
  1137.             pTP2PerfGlobals := NIL;
  1138.             END;
  1139. {$ENDC}
  1140.  
  1141.         InstallExceptionHandlers(FALSE);
  1142.  
  1143.         { Guarantee we can't be re-entered }
  1144.         pUDebugInitialized := FALSE;
  1145.         pCanEnterDebugger := FALSE;
  1146.  
  1147.         END;
  1148.     END;
  1149. {--------------------------------------------------------------------------------------------------}
  1150. {$S MADebugger}
  1151.  
  1152. PROCEDURE DebugFlag(flagAddr: BooleanPtr; flagChar: CHAR; theActionProc: ProcPtr; {CONST}
  1153.                     flagDesc: StringPtr);
  1154. { Register a BOOLEAN flag for the X debugger command;
  1155. flagAddr should be the address of the flag;
  1156. theActionProc should be a procPtr for a proc to be called to change the flag (optional).
  1157. flagChar should be the character to use in the debugger to toggle the flag;
  1158. desc should be a short description of the flag.
  1159. No checking is done for duplicate flagChars. }
  1160.  
  1161.     VAR
  1162.         theCount:            INTEGER;
  1163.         aDebugFEntry:        DebugFEntry;
  1164.  
  1165.     BEGIN
  1166.     WITH aDebugFEntry DO
  1167.         BEGIN
  1168.         addr := flagAddr;
  1169.         ch := UprChar(flagChar);
  1170.         actionProc := theActionProc;
  1171.         desc := NewString(flagDesc^);
  1172.         FailNil(desc);
  1173.         END;
  1174.     pFlagTable.InsertElementsBefore(pFlagTable.GetSize + 1, @aDebugFEntry, 1);
  1175.     END;
  1176. {--------------------------------------------------------------------------------------------------}
  1177. {$S MADebugger}
  1178.  
  1179. PROCEDURE DebugGlobalHandle(globAddr: Ptr; theActionProc: ProcPtr; {CONST}
  1180.                             globSym: MANamePtr);
  1181. { Register a symbol name of a global variable that contains a handle;
  1182. Case does not matter.  The global variable should contain a Handle.
  1183. The Action proc is a Function to be called to derive the handle if necessary. }
  1184.  
  1185.     VAR
  1186.         aDebugSEntry:        DebugSEntry;
  1187.  
  1188.     BEGIN
  1189.     WITH aDebugSEntry DO
  1190.         BEGIN
  1191.         addr := globAddr;
  1192.         actionProc := theActionProc;
  1193.         sym := globSym^;
  1194.         END;
  1195.     pSymTable.InsertElementsBefore(pSymTable.GetSize + 1, @aDebugSEntry, 1);
  1196.     END;
  1197.  
  1198. {--------------------------------------------------------------------------------------------------}
  1199. {$S MADebugger}
  1200.  
  1201. FUNCTION GetPromptedNames(prompt: StringPtr; VAR className, procName: MAName): BOOLEAN;
  1202.  
  1203.     VAR
  1204.         ch:                 CHAR;
  1205.         len:                INTEGER;
  1206.         s:                    Str255;
  1207.         i:                    INTEGER;
  1208.  
  1209.     PROCEDURE helpProc;
  1210.  
  1211.         BEGIN
  1212.         WriteLn;
  1213.         WriteLn('Please supply a ClassName.MethodName or MethodName or ProcName');
  1214.         WriteLn;
  1215.         END;
  1216.  
  1217.     BEGIN
  1218.     GetPromptedNames := FALSE;
  1219.  
  1220.     className := '';
  1221.     procName := '';
  1222.     len := 0;
  1223.  
  1224.     s := GetPromptedString(prompt, helpProc);
  1225.  
  1226.     FOR i := 1 TO length(s) DO
  1227.         BEGIN
  1228.         ch := UprChar(s[i]);
  1229.  
  1230.         IF ch IN ['A'..'Z', '0'..'9', '_', '%'] THEN
  1231.             BEGIN
  1232.             GetPromptedNames := TRUE;
  1233.             len := len + 1;
  1234.             procName[len] := ch;
  1235.             procName[0] := chr(len);
  1236.             END
  1237.         ELSE IF ch = '.' THEN
  1238.             BEGIN
  1239.             className := procName;
  1240.             procName := '';
  1241.             len := 0;
  1242.             END
  1243.         ELSE IF ch <> ' ' THEN
  1244.             BEGIN
  1245.             GetPromptedNames := FALSE;
  1246.             WriteLn(kDontKnow);
  1247.             Exit(GetPromptedNames);
  1248.             END;
  1249.         END;
  1250.     END;
  1251.  
  1252. {--------------------------------------------------------------------------------------------------}
  1253. {$S MADebugger}
  1254.  
  1255. FUNCTION GetPromptedValue(prompt: StringPtr; VAR asDecimal, asHex: Longint; symbolOK: BOOLEAN;
  1256.                           VAR gotSymbol: BOOLEAN): BOOLEAN;
  1257.  { returns TRUE iff a valid number is typed;
  1258.   if it returns FALSE but the parameters are 0, then the user typed only a return;
  1259.  
  1260.   if symbolOK is TRUE then a symbol is allowed, and gotSymbol will indicate if
  1261.   a symbol was typed }
  1262.  
  1263.     VAR
  1264.         ch:                 CHAR;
  1265.         digit:                INTEGER;
  1266.         anEvent:            EventRecord;
  1267.         s:                    Str255;
  1268.         i:                    INTEGER;
  1269.         sym:                Str255;
  1270.         done:                BOOLEAN;
  1271.         symbolTableSym:     Str255;
  1272.         gotNegation:        BOOLEAN;
  1273.  
  1274.     PROCEDURE helpProc;
  1275.  
  1276.         VAR
  1277.             i:                    INTEGER;
  1278.  
  1279.         FUNCTION DoSym(index: ArrayIndex): BOOLEAN;
  1280.  
  1281.             VAR
  1282.                 aDebugSEntry:        DebugSEntry;
  1283.  
  1284.             BEGIN
  1285.             pSymTable.GetElementsAt(index, @aDebugSEntry, 1);
  1286.             Write(aDebugSEntry.sym, ' ');
  1287.             DoSym := FALSE;
  1288.             END;
  1289.  
  1290.         BEGIN
  1291.         WriteLn;
  1292.         Write('Please supply a valid number');
  1293.         IF NOT symbolOK THEN
  1294.             WriteLn('.')
  1295.         ELSE
  1296.             BEGIN
  1297.             Write(' or one of the following symbols:');
  1298.             sym := kHelpRequest;
  1299.             asDecimal := CallSymbolLookup(sym, pSymbolProc);
  1300.             WriteLn;
  1301.  
  1302.             IF pSymTable.EachElementDoTil(DoSym, kIterateForward) = 0 THEN;
  1303.             WriteLn;
  1304.             END;
  1305.         END;
  1306.  
  1307.     FUNCTION DoSymSearch(index: ArrayIndex): BOOLEAN;
  1308.  
  1309.         VAR
  1310.             aDebugSEntry:        DebugSEntry;
  1311.  
  1312.         BEGIN
  1313.         pSymTable.GetElementsAt(index, @aDebugSEntry, 1);
  1314.         UprMAName(aDebugSEntry.sym);
  1315.         IF sym = aDebugSEntry.sym THEN
  1316.             BEGIN
  1317.             IF aDebugSEntry.addr = NIL THEN
  1318.                 asDecimal := Longint(CallSymActionProc(aDebugSEntry.actionProc))
  1319.             ELSE
  1320.                 asDecimal := LongIntPtr(aDebugSEntry.addr)^;
  1321.             DoSymSearch := TRUE;
  1322.             END
  1323.         ELSE
  1324.             DoSymSearch := FALSE;
  1325.         END;
  1326.  
  1327.     BEGIN
  1328.     asDecimal := 0;
  1329.     asHex := 0;
  1330.     gotSymbol := FALSE;
  1331.  
  1332.     s := GetPromptedString(prompt, helpProc);
  1333.     UprString(s, FALSE);
  1334.  
  1335.     IF s = '' THEN
  1336.         GetPromptedValue := FALSE
  1337.     ELSE
  1338.         BEGIN
  1339.         GetPromptedValue := TRUE;
  1340.  
  1341.         IF symbolOK & ((s[1] = '''') | NOT (s[1] IN ['-', '0'..'9', 'A'..'F'])) THEN
  1342.             BEGIN
  1343.             gotSymbol := TRUE;
  1344.  
  1345.             IF s[1] = '''' THEN
  1346.                 Delete(s, 1, 1);
  1347.  
  1348.             sym := s;
  1349.  
  1350.             asDecimal := CallSymbolLookup(sym, pSymbolProc);
  1351.  
  1352.             IF asDecimal = - 1 THEN                     { search local symbol table }
  1353.                 IF pSymTable.EachElementDoTil(DoSymSearch, kIterateForward) = 0 THEN;
  1354.             asHex := asDecimal;
  1355.  
  1356.             IF asHex = - 1 THEN
  1357.                 BEGIN
  1358.                 WriteLn(kDontKnow);
  1359.                 GetPromptedValue := FALSE;
  1360.                 END;
  1361.             END
  1362.         ELSE
  1363.             BEGIN
  1364.             gotNegation := FALSE;
  1365.             FOR i := 1 TO length(s) DO
  1366.                 BEGIN
  1367.                 ch := s[i];
  1368.  
  1369.                 digit := - 1;
  1370.                 IF ch IN ['0'..'9'] THEN
  1371.                     digit := ord(ch) - ord('0')
  1372.                 ELSE IF ch IN ['-'] THEN
  1373.                     gotNegation := TRUE
  1374.                 ELSE IF ch IN ['A'..'F'] THEN
  1375.                     BEGIN
  1376.                     digit := 10 + ord(ch) - ord('A');
  1377.                     asDecimal := - 1;
  1378.                     END
  1379.                 ELSE
  1380.                     BEGIN
  1381.                     asDecimal := - 1;
  1382.                     asHex := - 1;
  1383.                     WriteLn(kDontKnow);
  1384.                     GetPromptedValue := FALSE;
  1385.                     Exit(GetPromptedValue)
  1386.                     END;
  1387.  
  1388.                 IF digit >= 0 THEN
  1389.                     BEGIN
  1390.                     IF asDecimal >= 0 THEN
  1391.                         asDecimal := 10 * asDecimal + digit;
  1392.                     IF asHex >= 0 THEN
  1393.                         asHex := 16 * asHex + digit;
  1394.                     END;
  1395.                 END;
  1396.             IF gotNegation THEN
  1397.                 BEGIN
  1398.                 IF (asDecimal >= 0) THEN
  1399.                     asDecimal := - asDecimal;
  1400.                 IF asHex >= 0 THEN
  1401.                     asHex := - asHex;
  1402.                 END;
  1403.             END;
  1404.         END;
  1405.     END;
  1406.  
  1407. {--------------------------------------------------------------------------------------------------}
  1408. {$S MADebugger}
  1409.  
  1410. FUNCTION GetPromptedNumber(prompt: StringPtr; VAR asDecimal, asHex: Longint): BOOLEAN; { returns
  1411.     TRUE iff a valid number is typed; if it returns FALSE but the parameters are 0, then the user
  1412.     typed only a return }
  1413.  
  1414.     VAR
  1415.         symbol:             BOOLEAN;
  1416.  
  1417.     BEGIN
  1418.     GetPromptedNumber := GetPromptedValue(prompt, asDecimal, asHex, FALSE, symbol);
  1419.     END;
  1420.  
  1421. {--------------------------------------------------------------------------------------------------}
  1422. {$S MADebugger}
  1423.  
  1424. FUNCTION GetPromptedNumberWithDefault(prompt: StringPtr; default: INTEGER): INTEGER;
  1425. { Returns a number typed by the user.  Returns the default if a return is typed. }
  1426.  
  1427.     VAR
  1428.         s:                    Str255;
  1429.  
  1430.     BEGIN
  1431.     ConcatNumber(concat(prompt^, ' [default = '), default, s);
  1432.     s := concat(s, ']?:');
  1433.     IF GetPromptedNumber(@s, asDecimal, asHex) THEN
  1434.         GetPromptedNumberWithDefault := asDecimal
  1435.     ELSE
  1436.         GetPromptedNumberWithDefault := default;
  1437.     END;
  1438.  
  1439. {--------------------------------------------------------------------------------------------------}
  1440. {$S MADebugger}
  1441.  
  1442. FUNCTION GetPromptedStringWithDefault(prompt: StringPtr; default: StringPtr;
  1443.                                       PROCEDURE helpProc): Str255;
  1444. { Returns a string typed by the user.  Returns the default if a return is typed. }
  1445.  
  1446.     VAR
  1447.         s:                    Str255;
  1448.  
  1449.     BEGIN
  1450.     s := concat(prompt^, ' [default = ', default^, ']?:');
  1451.     s := GetPromptedString(@s, helpProc);
  1452.     IF s <> '' THEN
  1453.         GetPromptedStringWithDefault := s
  1454.     ELSE
  1455.         GetPromptedStringWithDefault := default^;
  1456.     END;
  1457.  
  1458. {--------------------------------------------------------------------------------------------------}
  1459. {$S MADebugger}
  1460. {$IFC IncludeDisassembler}
  1461.  
  1462. PROCEDURE ShowDisasmMemory(startAddress, numBytes: Longint);
  1463.  
  1464.     VAR
  1465.         idx:                INTEGER;
  1466.         BytesUsed:            INTEGER;
  1467.         opCode, Operand, Comment: DisAsmStr80;
  1468.  
  1469.     BEGIN
  1470.     WHILE numBytes > 0 DO
  1471.         BEGIN
  1472.         Disassembler(0, BytesUsed, startAddress, opCode, Operand, Comment, @Lookup);
  1473.         Write('    ');
  1474.         WritePtr(startAddress);
  1475.         Write(':  '); WriteLn(opCode, ' ', Operand, ' ', Comment);
  1476.         numBytes := numBytes - BytesUsed;
  1477.         startAddress := startAddress + BytesUsed;
  1478.         END;
  1479.     pMoreMem := startAddress;
  1480.     END;
  1481. {$EndC}
  1482.  
  1483. {$EndC}
  1484.  
  1485. {--------------------------------------------------------------------------------------------------}
  1486. {$S MADebugger}
  1487.  
  1488. PROCEDURE ShowMemory(startAddress, numBytes: Longint);
  1489.  
  1490.     VAR
  1491.         i:                    INTEGER;
  1492.         addr:                Longint;
  1493.         lines:                INTEGER;
  1494.         numeric:            STRING[40];
  1495.         ascii:                STRING[16];
  1496.         numPos:             INTEGER;
  1497.         ascPos:             INTEGER;
  1498.         decNumber:            Longint;
  1499.         chCode:             INTEGER;
  1500.         j:                    INTEGER;
  1501.  
  1502. {--------------------------------------------------------------------------------------------------}
  1503.  
  1504.     PROCEDURE BlankLine;
  1505.  
  1506.         CONST
  1507.             k8Spaces            = '        ';
  1508.  
  1509.         BEGIN
  1510.         ascii := concat(k8Spaces, k8Spaces);
  1511.         numeric := concat(ascii, ascii, k8Spaces);
  1512.         numPos := 0;
  1513.         ascPos := 0;
  1514.         END;
  1515.  
  1516. {--------------------------------------------------------------------------------------------------}
  1517.  
  1518.     PROCEDURE PrintLine;
  1519.  
  1520.         BEGIN
  1521.         WriteLn(numeric, '  ', ascii);
  1522.         END;
  1523.  
  1524.     BEGIN
  1525.     IF Odd(startAddress) THEN
  1526.         WriteLn('Odd Address')
  1527.     ELSE IF numBytes > 0 THEN
  1528.         BEGIN
  1529.         BlankLine;
  1530.  
  1531.         FOR i := 0 TO (numBytes - 1) DIV 2 DO
  1532.             BEGIN
  1533.             lines := 0;
  1534.             addr := startAddress + i + i;
  1535.  
  1536.             IF (i MOD 8) = 0 THEN
  1537.                 BEGIN
  1538.                 IF i > 0 THEN
  1539.                     BEGIN
  1540.                     PrintLine;
  1541.                     BlankLine;
  1542.                     lines := lines + 1;
  1543.                     END;
  1544.                 IF IsUserBreak | (lines > 20) THEN
  1545.                     BEGIN
  1546.                     WriteLn('More… [M]: ');
  1547.                     Exit(ShowMemory);
  1548.                     END;
  1549.                 Write('    ');
  1550.                 WritePtr(addr);
  1551.                 Write(':  ');
  1552.                 END;
  1553.  
  1554.             decNumber := IntegerPtr(addr)^;
  1555.             FOR j := 4 DOWNTO 1 DO
  1556.                 BEGIN
  1557.                 numeric[numPos + j] := kHexDigits[BAND(decNumber, 15) + 1];
  1558.                 decNumber := BSR(decNumber, 4);
  1559.                 END;
  1560.  
  1561.             decNumber := IntegerPtr(addr)^;
  1562.             FOR j := 2 DOWNTO 1 DO
  1563.                 BEGIN
  1564.                 chCode := BAND(decNumber, 255);
  1565.                 IF (chCode < $20) | (chCode > $D8) | (chCode = $7F) THEN { control, unassigned, or
  1566.                                                                           DEL }
  1567.                     chCode := ord('•');
  1568.                 ascii[ascPos + j] := chr(chCode);
  1569.                 decNumber := BSR(decNumber, 8);
  1570.                 END;
  1571.  
  1572.             numPos := numPos + 5;
  1573.             ascPos := ascPos + 2;
  1574.  
  1575.             pMoreMem := addr + 2;
  1576.             END;
  1577.  
  1578.         PrintLine;
  1579.         END;
  1580.     END;
  1581.  
  1582. {$IFC qDebug}
  1583. {--------------------------------------------------------------------------------------------------}
  1584. {$S MADebugger}
  1585.  
  1586. FUNCTION ShowHierarchy(obj: TObject; theClass: ObjClassID): Longint;
  1587.  
  1588.     VAR
  1589.         inspClass:            MAName;
  1590.         size:                Longint;
  1591.         super:                ObjClassID;
  1592.         shown:                INTEGER;
  1593.  
  1594.     BEGIN
  1595.     GetClassNameFromID(theClass, inspClass);            { srf 88.9.7 }
  1596.  
  1597.     IF inspClass = kInvalidObj THEN
  1598.         BEGIN
  1599.         size := GetHandleSize(Handle(obj));
  1600.         ShowMemory(ord(Handle(obj)^), size);
  1601.         END
  1602.     ELSE
  1603.         BEGIN
  1604.         size := GetClassSizeFromID(theClass);
  1605.         super := GetSuperClassID(theClass);
  1606.         IF super = kNilClass THEN                        { it is a root class, so skip class ptr }
  1607.             shown := sizeof(ObjClassID)
  1608.         ELSE
  1609.             shown := ShowHierarchy(obj, super);
  1610.         IF shown <= size THEN
  1611.             BEGIN
  1612.             GetClassNameFromID(theClass, inspClass);
  1613.             WriteLn(' ', inspClass);
  1614.             IF size > sizeof(ObjClassID) THEN            { don't show it if there are no fields }
  1615.                 ShowMemory(ord(Handle(obj)^) + shown, size - shown);
  1616.             END;
  1617.         END;
  1618.  
  1619.     ShowHierarchy := size;
  1620.     END;
  1621.  
  1622. {--------------------------------------------------------------------------------------------------}
  1623. {$S MADebugger}
  1624.  
  1625. PROCEDURE ShowFields(obj: TObject; doInspect: BOOLEAN);
  1626.  
  1627.     VAR
  1628.         i:                    Longint;
  1629.         s:                    Longint;
  1630.         objName:            MAName;
  1631.  
  1632.     BEGIN
  1633.     IF ord(obj) = - 1 THEN
  1634.         Write('')
  1635.     ELSE IF ord(obj) = - 2 THEN
  1636.         WriteLn('  No object at that level (not a method).')
  1637.     ELSE IF VerboseIsObject(obj) THEN
  1638.         BEGIN
  1639.         IF doInspect THEN
  1640.             BEGIN
  1641.             {###obj.Fields(pDebugView);}
  1642.             WriteLn;
  1643.             END
  1644.         ELSE
  1645.             BEGIN
  1646.             i := ShowHierarchy(obj, GetClassID(obj));
  1647.             s := GetHandleSize(Handle(obj));
  1648.             IF i < s THEN
  1649.                 BEGIN
  1650.                 WriteLn('rest of handle:');
  1651.                 ShowMemory(ord(Handle(obj)^) + i, s - i);
  1652.                 END;
  1653.             END;
  1654.         END;
  1655.     END;
  1656.  
  1657. {--------------------------------------------------------------------------------------------------}
  1658. {$S MAUtilitiesRes}                                     { Shouldn't be unloaded }
  1659.  
  1660. PROCEDURE GetLevel(level: INTEGER; topFrame: Longint; VAR calleeFrame, itsFrame: Longint);
  1661.  
  1662.     VAR
  1663.         i:                    INTEGER;
  1664.  
  1665.     BEGIN
  1666.     calleeFrame := topFrame;
  1667.     IF Odd(calleeFrame) THEN
  1668.         itsFrame := calleeFrame
  1669.     ELSE
  1670.         BEGIN
  1671.         itsFrame := LongIntPtr(calleeFrame)^;
  1672.         FOR i := 1 TO level DO
  1673.             IF Odd(itsFrame) | (itsFrame >= Longint(GetA5)) | (itsFrame <= calleeFrame) THEN
  1674.                 itsFrame := calleeFrame
  1675.             ELSE
  1676.                 BEGIN
  1677.                 calleeFrame := itsFrame;
  1678.                 itsFrame := LongIntPtr(itsFrame)^;
  1679.                 END;
  1680.         END;
  1681.     END;
  1682.  
  1683. {--------------------------------------------------------------------------------------------------}
  1684. {$S MADebugger}
  1685.  
  1686. PROCEDURE GetFrameInfo(calleeFrame: Longint; ppc: Longint; VAR callerFrame: Longint;
  1687.                        VAR itsFrame: Longint; VAR itsReceiver: TObject; VAR className: MAName;
  1688.                        VAR procName: MAName; VAR rcvrHandle: HexAddress; VAR rcvrClass: MAName;
  1689.                        VAR theSegNum: INTEGER);
  1690.  
  1691.     VAR
  1692.         aStringPtr:         StringPtr;
  1693.  
  1694.     BEGIN
  1695.     GetProcName(ppc, className, procName);
  1696.     theSegNum := GetSegFromPC(ppc);
  1697.  
  1698.     GetLevel(1, calleeFrame, itsFrame, callerFrame);
  1699.  
  1700.     rcvrClass := kInvalidObj;
  1701.     IF Odd(itsFrame) | (length(className) = 0) THEN
  1702.         BEGIN
  1703.         Longint(itsReceiver) := - 2;
  1704.         rcvrHandle := kInvalidObj;
  1705.         END
  1706.     ELSE
  1707.         BEGIN
  1708.         Longint(itsReceiver) := LongIntPtr(itsFrame + 8)^;
  1709.         aStringPtr := StringPtr(@rcvrHandle);
  1710.         PointerToHex(itsReceiver, aStringPtr^, 8);
  1711.         IF IsObject(itsReceiver) THEN
  1712.             GetClassNameFromID(GetClassID(itsReceiver), rcvrClass);
  1713.         END;
  1714.     END;
  1715.  
  1716. {--------------------------------------------------------------------------------------------------}
  1717. {$S MADebugger}
  1718.  
  1719. FUNCTION GetRcvrAtLevel(level: INTEGER; topFrame: Longint): TObject;
  1720.  
  1721.     VAR
  1722.         calleeFrame, callerFrame, itsFrame: Longint;
  1723.         receiver:            TObject;
  1724.         className, procName, rcvrClass: MAName;
  1725.         rcvrHandle:         HexAddress;
  1726.         dummy:                INTEGER;
  1727.  
  1728.     BEGIN
  1729.     itsFrame := topFrame;
  1730.     REPEAT
  1731.         calleeFrame := itsFrame;
  1732.         GetFrameInfo(calleeFrame, calleeFrame + 4, callerFrame, itsFrame, receiver, className,
  1733.                      procName, rcvrHandle, rcvrClass, dummy);
  1734.         level := level - 1;
  1735.     UNTIL (level < 0) | (calleeFrame = itsFrame);
  1736.     GetRcvrAtLevel := receiver;
  1737.     END;
  1738.  
  1739. {--------------------------------------------------------------------------------------------------}
  1740. {$S MADebugger}
  1741.  
  1742. PROCEDURE ShowLocals(level: INTEGER; topFrame: Longint);
  1743.  
  1744.     VAR
  1745.         startAt, finishAt:    Longint;
  1746.         itsFrame, calleeFrame: Longint;
  1747.  
  1748.     BEGIN
  1749.     GetLevel(level, topFrame, calleeFrame, itsFrame);
  1750.     startAt := max(calleeFrame + 8, itsFrame - 80);
  1751.     finishAt := itsFrame;
  1752.     ShowMemory(startAt, finishAt - startAt);
  1753.     IF pMoreMem >= finishAt THEN
  1754.         WriteLn('  The first locals declared appear last or are in reg''s.');
  1755.     END;
  1756.  
  1757. {
  1758.  calleeFrame: PREV LINK
  1759.  calleeFrame+4: PREV RA
  1760.  calleeFrame+8: PREV PARAMS
  1761.  MY LOCALS
  1762.  itsFrame: MY LINK
  1763.  itsFrame+4: MY RA
  1764.  itsFrame+8: MY PARAMS (IF A METHOD: callerFrame+8=SELF)
  1765.  NEXT LOCALS
  1766.  callerFrame: NEXT LINK
  1767.  }
  1768.  
  1769. {--------------------------------------------------------------------------------------------------}
  1770. {$S MADebugger}
  1771.  
  1772. PROCEDURE ShowParameters(level: INTEGER; topFrame: Longint);
  1773.  
  1774.     VAR
  1775.         startAt, finishAt:    Longint;
  1776.         itsFrame, callerFrame: Longint;
  1777.  
  1778.     BEGIN
  1779.     GetLevel(level + 1, topFrame, itsFrame, callerFrame);
  1780.     startAt := itsFrame + 8 + 4 * ord(ord(GetRcvrAtLevel(level, topFrame)) > 0);
  1781.     finishAt := Min(startAt + 80, callerFrame);
  1782.     WriteLn('  The last argument you declared is shown first below.');
  1783.     ShowMemory(startAt, finishAt - startAt);
  1784.     END;
  1785.  
  1786. {--------------------------------------------------------------------------------------------------}
  1787. {$S MADebugger}
  1788.  
  1789. PROCEDURE ShowNames(VAR procName: MAName; segNum: INTEGER);
  1790.  
  1791.     BEGIN
  1792.     Write(procName);
  1793.     IF segNum > 0 THEN
  1794.         Write(' Seg#: ', segNum: 1);
  1795.     END;
  1796.  
  1797. {--------------------------------------------------------------------------------------------------}
  1798. {$S MADebugger}
  1799.  
  1800. PROCEDURE ShowWhyInDebugger(aWhyInDebugger: WhyInDebugger; VAR procName: MAName; segNum: INTEGER);
  1801.  
  1802.     BEGIN
  1803.     CASE aWhyInDebugger OF
  1804.         tBegin:
  1805.             Write('Begin  ');
  1806.         tEnd:
  1807.             Write('End    ');
  1808.         tExit:
  1809.             Write('Exit   ');
  1810.         tBeginEndPair:
  1811.             Write('BegEnd ');
  1812.         tSysError:
  1813.             Write('SysErr ');
  1814.         tProgBreak:
  1815.             Write('Break  ');
  1816.         tVBL:
  1817.             Write('VBL Break  ');
  1818.     END;
  1819.  
  1820.     ShowNames(procName, segNum);
  1821.     END;
  1822.  
  1823. {--------------------------------------------------------------------------------------------------}
  1824. {$S MADebugger}
  1825.  
  1826. PROCEDURE ShowSymbolWhyInDebugger(aWhyInDebugger: WhyInDebugger; VAR procName: MAName;
  1827.                                   segNum: INTEGER);
  1828.  
  1829.     BEGIN
  1830.     CASE aWhyInDebugger OF
  1831.         tBegin:
  1832.             Write('>');
  1833.         tEnd:
  1834.             Write('<');
  1835.         tExit:
  1836.             Write('^ Exit: ');
  1837.         tBeginEndPair:
  1838.             Write('');
  1839.         tSysError:
  1840.             Write(': SysErr');
  1841.         tProgBreak:
  1842.             Write(': Break');
  1843.         tVBL:
  1844.             Write(': VBL Break');
  1845.     END;
  1846.     ShowNames(procName, segNum);
  1847.     END;
  1848.  
  1849. {--------------------------------------------------------------------------------------------------}
  1850. {$S MADebugger}
  1851.  
  1852. PROCEDURE ShowRecent;
  1853. { show recent history of pc.  Indents to show nesting level }
  1854.  
  1855.     CONST
  1856.         kIndentMax            = 31;                        { must be a power of 2 minus 1 }
  1857.         kIndentAmount        = 2;                        { number of spaces per nesting level }
  1858.         kDupClassName        = '=';
  1859.         kFailureProc        = 'FAILURE';
  1860.  
  1861.     VAR
  1862.         nextProcName, className, lastClassName: MAName;
  1863.         procName:            MAName;
  1864.         i:                    INTEGER;
  1865.         nexti:                INTEGER;
  1866.         pc:                 Longint;
  1867.         indentLevel, maxIndentLevel: INTEGER;
  1868.         aString:            Str255;
  1869.         aWhyInDebugger:     WhyInDebugger;
  1870.  
  1871.     BEGIN
  1872.     { get the maximum indenting or outdenting level first }
  1873.     maxIndentLevel := 0;
  1874.     i := BAND(pRecentIndex + 1, kRecent);                { absolute value, modulo kRecent }
  1875.     REPEAT
  1876.         WITH pRecentPC[i] DO
  1877.             IF thePC <> 0 THEN
  1878.                 BEGIN
  1879.                 CASE theWhyInDebugger OF
  1880.                     tBegin:
  1881.                         maxIndentLevel := maxIndentLevel + kIndentAmount;
  1882.                     tEnd, tBeginEndPair:
  1883.                         maxIndentLevel := maxIndentLevel - kIndentAmount;
  1884.                     tExit: ;
  1885.                 END;
  1886.                 END;
  1887.         i := BAND(i + 1, kRecent);                        { absolute value, modulo kRecent }
  1888.     UNTIL i = pRecentIndex;
  1889.  
  1890.     { try to intelligently set a starting indent level }
  1891.     IF maxIndentLevel < 0 THEN                            { some outdenting required }
  1892.         indentLevel := Min(abs(maxIndentLevel), (kIndentMax + 1) DIV 2)
  1893.     ELSE
  1894.         indentLevel := 0;                                { only indents }
  1895.  
  1896.     lastClassName := '';
  1897.     aString := '| | | | | | | | | | | | | | | ';
  1898.     i := BAND(pRecentIndex + 1, kRecent);                { absolute value, modulo kRecent }
  1899.     REPEAT
  1900.         WITH pRecentPC[i] DO
  1901.             IF thePC <> 0 THEN
  1902.                 BEGIN
  1903.                 GetProcName(ord(@thePC), className, procName);
  1904.                 aWhyInDebugger := theWhyInDebugger;
  1905.                 nexti := BAND(i + 1, kRecent);
  1906.                 IF nexti <> pRecentIndex THEN
  1907.                     BEGIN
  1908.                     GetMethodName(ord(@pRecentPC[nexti].thePC), nextProcName);
  1909.                     IF nextProcName = procName THEN
  1910.                         BEGIN
  1911.                         aWhyInDebugger := tBeginEndPair;
  1912.                         i := nexti;
  1913.                         END;
  1914.                     END;
  1915.                 CASE aWhyInDebugger OF
  1916.                     tBegin, tBeginEndPair:
  1917.                         indentLevel := BAND(indentLevel + kIndentAmount, kIndentMax);
  1918.                 END;
  1919.                 aString[0] := chr(indentLevel);
  1920.                 Write(aString);
  1921.                 CASE aWhyInDebugger OF
  1922.                     tEnd, tBeginEndPair:
  1923.                         indentLevel := BAND(indentLevel - kIndentAmount, kIndentMax);
  1924.                     tExit: ;
  1925.                 END;
  1926.                 IF (lastClassName = className) & (length(className) <> 0) THEN
  1927.                     BEGIN
  1928.                     Delete(procName, 1, length(className));
  1929.                     insert(kDupClassName, procName, 1);
  1930.                     END;
  1931.                 lastClassName := className;
  1932.                 ShowSymbolWhyInDebugger(aWhyInDebugger, procName, - 1);
  1933.                 WriteLn;
  1934.                 IF (aWhyInDebugger = tExit) | ((length(className) = 0) & (procName =
  1935.                    kFailureProc)) THEN
  1936.                     WriteLn('------------------------------');
  1937.                 END;
  1938.         i := BAND(i + 1, kRecent);                        { absolute value, modulo kRecent }
  1939.     UNTIL i = pRecentIndex;
  1940.     WriteLn;
  1941.  
  1942.     pMoreMem := - 1;
  1943.     END;
  1944.  
  1945. {--------------------------------------------------------------------------------------------------}
  1946. {$S MADebugger}
  1947.  
  1948. PROCEDURE ShowStack;
  1949.  
  1950.     VAR
  1951.         startLevel:         INTEGER;
  1952.         interrupted:        BOOLEAN;
  1953.     {??? moved strings out to this level to help reduce the stack rqs of recursion.
  1954.     Eventually should fix even better than this ???}
  1955.         className:            MAName;
  1956.         procName:            MAName;
  1957.         rcvrClass:            MAName;
  1958.         rcvrHandle:         HexAddress;
  1959.  
  1960.     PROCEDURE ShowLevel(level: INTEGER; calleeFrame, ppc: Longint);
  1961.  
  1962.         VAR
  1963.             callerFrame:        Longint;
  1964.             itsFrame:            Longint;
  1965.             receiver:            TObject;
  1966.             segNum:             INTEGER;
  1967.  
  1968.         BEGIN
  1969.         GetFrameInfo(calleeFrame, ppc, callerFrame, itsFrame, receiver, className, procName,
  1970.                      rcvrHandle, rcvrClass, segNum);
  1971.  
  1972.         IF calleeFrame <> itsFrame THEN
  1973.             BEGIN
  1974.             nextLevel := level + 1;
  1975.             nextFrame := itsFrame;
  1976.             pNextPC := itsFrame + 4;
  1977.             ShowLevel(nextLevel, nextFrame, pNextPC)
  1978.             END;
  1979.  
  1980.         Write(' ', level: 3, ' ');
  1981.         WritePtr(calleeFrame);
  1982.         Write(': ');
  1983.  
  1984.         { retrieve names for this frame again }
  1985.         GetFrameInfo(calleeFrame, ppc, callerFrame, itsFrame, receiver, className, procName,
  1986.                      rcvrHandle, rcvrClass, segNum);
  1987.  
  1988.         ShowNames(procName, segNum);
  1989.         IF ord(receiver) > 0 THEN
  1990.             Write('  Self: ', rcvrHandle, ' is ', rcvrClass);
  1991.         WriteLn;
  1992.         END;
  1993.  
  1994.     BEGIN
  1995.     pMoreMem := - 1;
  1996.     startLevel := nextLevel;
  1997.  
  1998.     ShowLevel(startLevel, nextFrame, pNextPC);
  1999.     END;
  2000.  
  2001. {--------------------------------------------------------------------------------------------------}
  2002. {$S MAUtilitiesRes}                                     { Shouldn't be unloaded }
  2003. {$Push} {$Z+}
  2004.  
  2005. PROCEDURE EachFrameDo(calleeFrame, ppc: Longint; PROCEDURE
  2006.                       DoToFrame(calleeFrame: Longint; ppc: Longint; callerFrame: Longint;
  2007.                                 itsFrame: Longint));
  2008.  
  2009.     PROCEDURE DoLevel(calleeFrame, ppc: Longint);
  2010.  
  2011.         VAR
  2012.             callerFrame:        Longint;
  2013.             itsFrame:            Longint;
  2014.             nextFrame:            Longint;
  2015.             pNextPC:            Longint;
  2016.  
  2017.         BEGIN
  2018.         GetLevel(1, calleeFrame, itsFrame, callerFrame);
  2019.         DoToFrame(calleeFrame, ppc, callerFrame, itsFrame);
  2020.         IF calleeFrame <> itsFrame THEN
  2021.             BEGIN
  2022.             nextFrame := itsFrame;
  2023.             pNextPC := itsFrame + 4;
  2024.             DoLevel(nextFrame, pNextPC)
  2025.             END;
  2026.         END;
  2027.  
  2028.     BEGIN
  2029.     DoLevel(calleeFrame, ppc);
  2030.     END;
  2031. {$Pop}
  2032.  
  2033. {--------------------------------------------------------------------------------------------------}
  2034. {$S MADebugger}
  2035.  
  2036. PROCEDURE ShowTempSpace(VAR lockedSpace, totalSpace: Longint);
  2037.  
  2038.     VAR
  2039.         seg:                Handle;
  2040.  
  2041.     BEGIN
  2042.     lockedSpace := TotalTempSize(TRUE, seg);
  2043.     totalSpace := TotalTempSize(FALSE, seg);
  2044.  
  2045.     WriteLn('  Current temp space: locked = ', lockedSpace: 1, ', unlocked = ', totalSpace -
  2046.             lockedSpace: 1, ', total = ', totalSpace: 1);
  2047.  
  2048.     END;
  2049.  
  2050. {--------------------------------------------------------------------------------------------------}
  2051. {$S MADebugger}
  2052.  
  2053. PROCEDURE ShowHeapInfo;
  2054.  
  2055.     VAR
  2056.         codeRes:            Longint;
  2057.         codeShort:            Longint;
  2058.         lockedSpace:        Longint;
  2059.         lowSpaceRes:        Longint;
  2060.         okCode:             BOOLEAN;
  2061.         okLowSpace:         BOOLEAN;
  2062.         oldPerm:            BOOLEAN;
  2063.         oldRsrcUse:         Longint;
  2064.         purgeSpace:         Longint;
  2065.         totalSpace:         Longint;
  2066.  
  2067.     BEGIN
  2068.     oldRsrcUse := gMaxLockedRsrc;
  2069.  
  2070.     {== S T A C K ==}
  2071.     WriteLn('STACK');
  2072.     WriteLn('  Current total stack = ', pStackSpace: 1, '           Maximum stack used = ',
  2073.             gMaxStackDepth: 1);
  2074.     WriteLn('  Current procedure stack = ', pProcStack: 1, '           Available stack = ',
  2075.             ord(GetCurStackBase) - ord(GetApplLimit): 1);
  2076.  
  2077.     IF pBreakStack < $7FFFFFFF THEN
  2078.         WriteLn('Break at total stack space = ', pBreakStack: 1);
  2079.     IF pBrProcStack < $7FFFFFFF THEN
  2080.         WriteLn('Break at procedure stack space = ', pBrProcStack: 1);
  2081.  
  2082.     {== R E S E R V E S ==}
  2083.     WriteLn('RESERVES');
  2084.     DoChangeReserve(FALSE, codeRes, codeShort, lowSpaceRes, okCode, okLowSpace);
  2085.  
  2086.     Write('  code = ', codeRes: 1);
  2087.     IF okCode THEN
  2088.         Write(' (OK)')
  2089.     ELSE
  2090.         Write(' (low: ', codeShort: 1, ')');
  2091.  
  2092.     Write('     low space = ', lowSpaceRes: 1);
  2093.     IF okLowSpace THEN
  2094.         Write(' (OK)')
  2095.     ELSE
  2096.         Write(' (gone)');
  2097.  
  2098.     Write('  allocation flag: ');
  2099.     IF pPermFlag THEN
  2100.         WriteLn('permanent')
  2101.     ELSE
  2102.         WriteLn('temporary');
  2103.  
  2104.     {== T E M P  S P A C E ==}
  2105.     WriteLn('TEMP SPACE');
  2106.     ShowTempSpace(lockedSpace, totalSpace);
  2107.  
  2108.     purgeSpace := totalSpace - codeRes;
  2109.     IF purgeSpace > (totalSpace - lockedSpace) THEN
  2110.         purgeSpace := totalSpace - lockedSpace;
  2111.  
  2112.     IF purgeSpace >= 0 THEN
  2113.         WriteLn('  Purgeable temp space = ', purgeSpace: 1)
  2114.     ELSE
  2115.         WriteLn('  Needed reserve handle size = ', - purgeSpace: 1);
  2116.  
  2117.     {== O T H E R ==}
  2118.     WriteLn('OTHER');
  2119.     CheckRsrcUsage;
  2120.  
  2121.     Write('  Max resource usage = ', gMaxLockedRsrc: 1);
  2122.     IF oldRsrcUse <> gMaxLockedRsrc THEN
  2123.         WriteLn(' (new)')
  2124.     ELSE
  2125.         WriteLn;
  2126.  
  2127.     gMaxLockedRsrc := oldRsrcUse;                        { so we get the '(new)' indications again }
  2128.  
  2129.     oldPerm := PermAllocation(TRUE);
  2130.     totalSpace := FreeMem;
  2131.     oldPerm := PermAllocation(oldPerm);
  2132.  
  2133.     WriteLn('  (permanent) FreeMem = ', totalSpace: 1, '            Free master pointers = ',
  2134.             GetFreeMastersCount: 1);
  2135.     END;
  2136.  
  2137. {--------------------------------------------------------------------------------------------------}
  2138. {$S MADebugger}
  2139.  
  2140. PROCEDURE HeapCmd;
  2141.  
  2142.     VAR
  2143.         ch:                 CHAR;
  2144.         decNum:             Longint;
  2145.         done:                BOOLEAN;
  2146.         hexNum:             Longint;
  2147.         x:                    Longint;
  2148.         y:                    Longint;
  2149.  
  2150.         id:                 INTEGER;
  2151.         name:                Str255;
  2152.         nSeg:                INTEGER;
  2153.         seg:                Handle;
  2154.         t:                    ResType;
  2155.  
  2156.         codeRes:            Longint;
  2157.         codeShort:            Longint;
  2158.         lowSpaceRes:        Longint;
  2159.         okCode:             BOOLEAN;
  2160.         okLowSpace:         BOOLEAN;
  2161.         oldPerm:            BOOLEAN;
  2162.  
  2163.     PROCEDURE helpProc;
  2164.  
  2165.         BEGIN
  2166.         WriteLn;
  2167.         WriteLn('+ -- set breakpoint on procedure stack usage');
  2168.         WriteLn('B -- set breakpoint on total stack usage');
  2169.         WriteLn('D -- reset maximum stack depth');
  2170.         WriteLn('I -- show heap/stack info');
  2171.         WriteLn('M -- show heap/stack info AND MaxMem');
  2172.         WriteLn('R -- show/set heap reserve');
  2173.         WriteLn('S -- list LOADED segments');
  2174.         WriteLn('ß (option-S) -- list ALL segments');
  2175.         WriteLn;
  2176.         END;
  2177.  
  2178.     PROCEDURE ShowSegments(allSegments: BOOLEAN);
  2179.     { Show segment information.  if allSegments is true then also show unloaded & purged }
  2180.  
  2181.         VAR
  2182.             i:                    INTEGER;
  2183.  
  2184.         BEGIN
  2185.         codeRes := 0;                                    { counts size of code segments }
  2186.  
  2187.         nSeg := GetHandleSize(Handle(gCodeSegs)) DIV sizeof(Handle);
  2188.  
  2189.         WriteLn('Total # segments = ', nSeg: 1);
  2190.         IF allSegments THEN
  2191.             WriteLn(
  2192.         '• = resident, L = loaded, U = unloaded (and relocatable), '' '' = purged (or never loaded)'
  2193.                     )
  2194.         ELSE
  2195.             WriteLn('• = resident, L = loaded');
  2196.  
  2197.         FOR i := 1 TO nSeg DO
  2198.             BEGIN
  2199.             seg := gCodeSegs^^[i];
  2200.             IF allSegments | (NOT IsHandlePurged(seg) & isHandleLocked(seg)) THEN
  2201.                 BEGIN
  2202.                 GetResInfo(seg, id, t, name);
  2203.  
  2204.                 WritePtr(seg);
  2205.  
  2206.                 Write('  Seg#:', id: 3, ' ');
  2207.  
  2208.                 IF gIsResidentSeg^^[i] THEN
  2209.                     Write('• ')
  2210.                 ELSE IF IsHandlePurged(seg) THEN
  2211.                     Write('  ')
  2212.                 ELSE IF gIsLoadedSeg^^[i] THEN
  2213.                     Write('L ')
  2214.                 ELSE
  2215.                     Write('U ');
  2216.  
  2217.                 Write(name, ' ': 25 - length(name), ' ');
  2218.  
  2219.                 WriteLn(pSegSize^^[i]: 6, ' bytes');
  2220.  
  2221.                 codeRes := codeRes + pSegSize^^[i] + 8;
  2222.                 END;
  2223.             END;
  2224.  
  2225.         WriteLn;
  2226.         WriteLn('Total loaded code = ', codeRes: 1);
  2227.         ShowTempSpace(x, y);
  2228.         END;
  2229.  
  2230.     BEGIN
  2231.     done := FALSE;
  2232.     REPEAT
  2233.         ch := GetPromptedChar(AtStr('Heap/Stack Cmd'), AtStr('+BDIMRSß'), helpProc);
  2234.  
  2235.         CASE ch OF
  2236.             '+':
  2237.                 BEGIN
  2238.                 IF GetPromptedNumber(AtStr('Break at what procedure stack usage?: '), decNum,
  2239.                                      hexNum) THEN
  2240.  
  2241.                     IF decNum = 0 THEN
  2242.                         pBrProcStack := $7FFFFFFF
  2243.                     ELSE IF decNum > 0 THEN
  2244.                         pBrProcStack := decNum;
  2245.  
  2246.                 ShowHeapInfo;
  2247.  
  2248.                 done := TRUE;
  2249.                 END;
  2250.  
  2251.             'B':
  2252.                 BEGIN
  2253.                 IF GetPromptedNumber(AtStr('Break at what total stack usage?: '), decNum,
  2254.                    hexNum) THEN
  2255.                     IF decNum = 0 THEN
  2256.                         pBreakStack := $7FFFFFFF
  2257.                     ELSE IF decNum > 0 THEN
  2258.                         pBreakStack := decNum;
  2259.  
  2260.                 ShowHeapInfo;
  2261.  
  2262.                 done := TRUE;
  2263.                 END;
  2264.  
  2265.             'D':
  2266.                 BEGIN
  2267.                 gMaxStackDepth := - 1;
  2268.  
  2269.                 ShowHeapInfo;
  2270.  
  2271.                 done := TRUE;
  2272.                 END;
  2273.  
  2274.             'I':
  2275.                 BEGIN
  2276.                 ShowHeapInfo;
  2277.                 done := TRUE;
  2278.                 END;
  2279.  
  2280.             'M':
  2281.                 BEGIN
  2282.                 oldPerm := PermAllocation(TRUE);
  2283.                 x := MaxMem(decNum);
  2284.                 oldPerm := PermAllocation(oldPerm);
  2285.  
  2286.                 ShowHeapInfo;
  2287.  
  2288.                 WriteLn('(permanent) MaxMem = ', x: 1);
  2289.  
  2290.                 done := TRUE;
  2291.                 END;
  2292.  
  2293.             'R':
  2294.                 BEGIN
  2295.                 DoChangeReserve(TRUE, codeRes, codeShort, lowSpaceRes, okCode, okLowSpace);
  2296.                 ShowHeapInfo;
  2297.                 done := TRUE;
  2298.                 END;
  2299.  
  2300.             'S':
  2301.                 BEGIN
  2302.                 ShowSegments(FALSE);
  2303.  
  2304.                 done := TRUE;
  2305.                 END;
  2306.  
  2307.             'ß':
  2308.                 BEGIN
  2309.                 ShowSegments(TRUE);
  2310.  
  2311.                 done := TRUE;
  2312.                 END;
  2313.  
  2314.             OTHERWISE
  2315.                 done := TRUE;
  2316.         END;
  2317.     UNTIL done;
  2318.     END;
  2319.  
  2320. {--------------------------------------------------------------------------------------------------}
  2321. {$S MADebugger}
  2322.  
  2323. PROCEDURE SetBreakCmd;
  2324.  
  2325.     VAR
  2326.         done:                BOOLEAN;
  2327.         ch:                 CHAR;
  2328.         aClassName, aProcName: MAName;
  2329.  
  2330.     BEGIN
  2331.     IF pBreakCount < 10 THEN
  2332.         BEGIN
  2333.         IF GetPromptedNames(AtStr('Break at [Typename.ProcName or ProcName]?: '), aClassName,
  2334.                             aProcName) THEN
  2335.             BEGIN
  2336.             pBreakCount := pBreakCount + 1;
  2337.             pBreakClass[pBreakCount] := aClassName;
  2338.             pBreakProc[pBreakCount] := aProcName;
  2339.             END
  2340.         END
  2341.     ELSE
  2342.         WriteLn('Already have maximum breakpoints set!');
  2343.     END;
  2344.  
  2345. {--------------------------------------------------------------------------------------------------}
  2346. {$S MADebugger}
  2347.  
  2348. PROCEDURE ClrBreakCmd;
  2349.  
  2350.     VAR
  2351.         aString:            Str255;
  2352.         whichBreak:         Longint;
  2353.  
  2354.     PROCEDURE ClrBreakHelp;
  2355.  
  2356.         VAR
  2357.             i:                    INTEGER;
  2358.  
  2359.         BEGIN
  2360.         WriteLn;
  2361.         WriteLn('A - All breakpoints');
  2362.         FOR i := 1 TO pBreakCount DO
  2363.             BEGIN
  2364.             Write(i: 1, ' - ');
  2365.             IF pBreakClass[i] <> '' THEN
  2366.                 WriteLn(pBreakClass[i], '.', pBreakProc[i])
  2367.             ELSE
  2368.                 WriteLn(pBreakProc[i]);
  2369.             END;
  2370.         END;
  2371.  
  2372.     BEGIN
  2373.     CASE pBreakCount OF
  2374.         0:
  2375.             WriteLn('No breakpoints are set!.');
  2376.         1:
  2377.             BEGIN
  2378.             pBreakCount := 0;
  2379.             WriteLn('Cleared the breakpoint.');
  2380.             END;
  2381.         OTHERWISE
  2382.             BEGIN
  2383.             ConcatNumber('Which breakpoint[1-', pBreakCount, aString);
  2384.             aString := concat(aString, ',A]?:');
  2385.             aString := GetPromptedString(@aString, ClrBreakHelp);
  2386.             UprStr255(aString);
  2387.             IF aString = 'A' THEN
  2388.                 BEGIN
  2389.                 pBreakCount := 0;
  2390.                 WriteLn('Cleared all the breakpoints.');
  2391.                 END
  2392.             ELSE IF aString <> '' THEN
  2393.                 BEGIN
  2394.                 StringToNum(aString, whichBreak);
  2395.                 IF (whichBreak > 0) & (whichBreak <= pBreakCount) THEN
  2396.                     BEGIN
  2397.                     WHILE whichBreak < pBreakCount DO
  2398.                         BEGIN
  2399.                         pBreakClass[whichBreak] := pBreakClass[whichBreak + 1];
  2400.                         pBreakProc[whichBreak] := pBreakProc[whichBreak + 1];
  2401.                         whichBreak := whichBreak + 1;
  2402.                         END;
  2403.                     pBreakCount := pBreakCount - 1;
  2404.                     WriteLn('Cleared the breakpoint.');
  2405.                     END;
  2406.                 END;
  2407.             END;
  2408.     END;
  2409.     END;
  2410.  
  2411. {--------------------------------------------------------------------------------------------------}
  2412. {$Ifc qPerform}
  2413. {$S MADebugger}
  2414.  
  2415. PROCEDURE PerfCmd;
  2416.  
  2417.     VAR
  2418.         done:                BOOLEAN;
  2419.         ch:                 CHAR;
  2420.         aBool:                BOOLEAN;
  2421.         perfErr:            INTEGER;
  2422.         s:                    Str255;
  2423.         ms:                 INTEGER;
  2424.         apName:             Str255;
  2425.         apRefnum:            INTEGER;
  2426.         apParam:            Handle;
  2427.  
  2428.     PROCEDURE helpProc;
  2429.  
  2430.         BEGIN
  2431.         WriteLn;
  2432.         WriteLn('D -- Dump to output file');
  2433.         WriteLn('E -- End the tools and free their storage');
  2434.         WriteLn('I -- Init performance tools');
  2435.         WriteLn('T -- Toggle tools on and off');
  2436.         WriteLn;
  2437.         END;
  2438.  
  2439.     PROCEDURE appCodeTypeHelpProc;
  2440.  
  2441.         BEGIN
  2442.         WriteLn;
  2443.         WriteLn('Please specify the resource type to measure');
  2444.         WriteLn;
  2445.         END;
  2446.  
  2447.     PROCEDURE romNameHelpProc;
  2448.  
  2449.         BEGIN
  2450.         WriteLn;
  2451.         WriteLn('Please specify the ROM name');
  2452.         WriteLn;
  2453.         END;
  2454.  
  2455.     PROCEDURE reportFileHelpProc;
  2456.  
  2457.         BEGIN
  2458.         WriteLn;
  2459.         WriteLn('Please specify a file name for the report');
  2460.         WriteLn;
  2461.         END;
  2462.  
  2463.     BEGIN
  2464.     done := FALSE;
  2465.     REPEAT
  2466.         ch := GetPromptedChar(AtStr('Performance Cmd'), AtStr('DEIT'), helpProc);
  2467.  
  2468.         CASE ch OF
  2469.             'D':
  2470.                 BEGIN
  2471.                 IF pTP2PerfGlobals <> NIL THEN
  2472.                     BEGIN
  2473.                     WriteLn('Dump performance tools data.  Press Return to take the default…');
  2474.                     GetAppParms(apName, apRefnum, apParam);
  2475.                     s := concat(apName, '.perf');
  2476.                     perfErr := PerfDump(pTP2PerfGlobals,
  2477.                                         GetPromptedStringWithDefault(AtStr('  reportFile'), @s,
  2478.                                         reportFileHelpProc), GetPromptedNumberWithDefault(AtStr(
  2479.                                         '  doHistogram (TRUE=1/FALSE=0)'), 0) = 1,
  2480.                                         GetPromptedNumberWithDefault(AtStr('  rptFileColumns'),
  2481.                                80));
  2482.                     IF perfErr <> NoErr THEN
  2483.                         WriteLn('Error: ', perfErr, ' while dumping');
  2484.                     END
  2485.                 ELSE
  2486.                     WriteLn('Not initialized!');
  2487.                 done := TRUE;
  2488.                 END;
  2489.             'E':
  2490.                 BEGIN
  2491.                 IF pTP2PerfGlobals <> NIL THEN
  2492.                     BEGIN
  2493.                     TermPerf(pTP2PerfGlobals);
  2494.                     pTP2PerfGlobals := NIL;
  2495.                     END
  2496.                 ELSE
  2497.                     WriteLn('Not initialized!');
  2498.                 done := TRUE;
  2499.                 END;
  2500.             'I':
  2501.                 BEGIN
  2502.                 IF pTP2PerfGlobals = NIL THEN
  2503.                     BEGIN
  2504.                     WriteLn('Init performance tools.  Press Return to take the default…');
  2505.                     { set the default }
  2506.                     CASE gConfiguration.machineType OF
  2507.                         gestaltClassic, gestaltMacXL, gestaltMac512KE, gestaltMacPlus, gestaltMacSE:
  2508.                             ms := 10;
  2509.                         OTHERWISE
  2510.                             ms := 4;
  2511.                     END;
  2512.                     aBool := InitPerf(pTP2PerfGlobals,
  2513.                                       GetPromptedNumberWithDefault(AtStr('  timerCount'), ms),
  2514.                                       GetPromptedNumberWithDefault(AtStr('  codeAndROMBucketSize'),
  2515.                                                                    8),
  2516.                                       GetPromptedNumberWithDefault(AtStr('  doROM (TRUE=1/FALSE=0)'
  2517.                                                                           ), 0) = 1,
  2518.                                       GetPromptedNumberWithDefault(AtStr(
  2519.                                                                       '  doAppCode (TRUE=1/FALSE=0)'
  2520.                                                                          ), 1) = 1,
  2521.                                       GetPromptedStringWithDefault(AtStr('  appCodeType'),
  2522.                                                                    AtStr('CODE'),
  2523.                                                                    appCodeTypeHelpProc),
  2524.                                       GetPromptedNumberWithDefault(AtStr('  romID'), 0),
  2525.                                       GetPromptedStringWithDefault(AtStr('  romName'), AtStr(''),
  2526.                                                                    romNameHelpProc),
  2527.                                       GetPromptedNumberWithDefault(AtStr('  doRAM (TRUE=1/FALSE=0)')
  2528.                                                                    , 0) = 1,
  2529.                                       GetPromptedNumberWithDefault(AtStr('  ramLow'), 0),
  2530.                                       GetPromptedNumberWithDefault(AtStr('  ramHigh'), 0),
  2531.                                       GetPromptedNumberWithDefault(AtStr('  ramBucketSize'), 8));
  2532.                     IF NOT aBool THEN
  2533.                         WriteLn('Performance tools initialization FAILED.');
  2534.                     END
  2535.                 ELSE
  2536.                     WriteLn('Already initialized!');
  2537.  
  2538.                 done := TRUE;
  2539.                 END;
  2540.             'T':
  2541.                 BEGIN
  2542.                 IF pTP2PerfGlobals <> NIL THEN
  2543.                     BEGIN
  2544.                     oldState := NOT oldState;
  2545.                     END
  2546.                 ELSE
  2547.                     WriteLn('Not initialized!');
  2548.                 done := TRUE;
  2549.                 END;
  2550.             OTHERWISE
  2551.                 done := TRUE;
  2552.         END;
  2553.     UNTIL done;
  2554.     END;
  2555. {$Endc}
  2556. {--------------------------------------------------------------------------------------------------}
  2557. {$S MADebugger}
  2558.  
  2559. PROCEDURE FlagCmd;
  2560.  
  2561.     VAR
  2562.         done:                BOOLEAN;
  2563.         ch:                 CHAR;
  2564.         i:                    INTEGER;
  2565.         theFlags:            Str255;
  2566.         newState:            BOOLEAN;
  2567.         theCount:            INTEGER;
  2568.         actionCh:            CHAR;
  2569.  
  2570.     PROCEDURE FlagInfo(desc: StringHandle; addr: BooleanPtr);
  2571.  
  2572.         BEGIN
  2573.         HLock(Handle(desc));
  2574. {$Push} {$H-}
  2575.         Write(desc^^, ': ');
  2576. {$Pop}
  2577.         HUnLock(Handle(desc));
  2578.         IF addr^ THEN
  2579.             WriteLn('TRUE')
  2580.         ELSE
  2581.             WriteLn('FALSE');
  2582.         END;
  2583.  
  2584.     PROCEDURE helpProc;
  2585.  
  2586.         VAR
  2587.             i:                    INTEGER;
  2588.  
  2589.         FUNCTION DoFlag(index: ArrayIndex): BOOLEAN;
  2590.  
  2591.             VAR
  2592.                 aDebugFEntry:        DebugFEntry;
  2593.  
  2594.             BEGIN
  2595.             pFlagTable.GetElementsAt(index, @aDebugFEntry, 1);
  2596.             WITH aDebugFEntry DO
  2597.                 BEGIN
  2598.                 Write(ch, ' -- ');
  2599.                 FlagInfo(desc, addr);
  2600.                 END;
  2601.             DoFlag := FALSE;
  2602.             END;
  2603.  
  2604.         BEGIN
  2605.         WriteLn;
  2606.         IF pFlagTable.EachElementDoTil(DoFlag, kIterateForward) = 0 THEN;
  2607.         WriteLn;
  2608.         END;
  2609.  
  2610.     FUNCTION DoFlagCase(index: ArrayIndex): BOOLEAN;
  2611.  
  2612.         VAR
  2613.             aDebugFEntry:        DebugFEntry;
  2614.  
  2615.         BEGIN
  2616.         pFlagTable.GetElementsAt(index, @aDebugFEntry, 1);
  2617.         WITH aDebugFEntry DO
  2618.             BEGIN
  2619.             IF addr^ THEN
  2620.                 theFlags[length(theFlags) + 1] := UprChar(ch)
  2621.             ELSE
  2622.                 theFlags[length(theFlags) + 1] := LowerChar(ch);
  2623.             theFlags[0] := chr(length(theFlags) + 1);
  2624.             END;
  2625.         DoFlagCase := FALSE;
  2626.         END;
  2627.  
  2628.     FUNCTION DoFlagAction(index: ArrayIndex): BOOLEAN;
  2629.  
  2630.         VAR
  2631.             aDebugFEntry:        DebugFEntry;
  2632.  
  2633.         BEGIN
  2634.         pFlagTable.GetElementsAt(index, @aDebugFEntry, 1);
  2635.         WITH aDebugFEntry DO
  2636.             BEGIN
  2637.             IF ch = actionCh THEN
  2638.                 BEGIN
  2639.                 newState := NOT addr^;
  2640.                 IF actionProc <> NIL THEN
  2641.                     IF CallFlagActionProc(newState, actionProc) THEN; { discard result }
  2642.                 addr^ := newState;
  2643.                 FlagInfo(desc, addr);
  2644.                 pFlagTable.ReplaceElementsAt(index, @aDebugFEntry, 1);
  2645.                 DoFlagAction := TRUE;
  2646.                 END;
  2647.             END;
  2648.         DoFlagAction := FALSE;
  2649.         END;
  2650.  
  2651.     BEGIN
  2652.     done := FALSE;
  2653.     REPEAT
  2654.         { Set the display case correctly on all the flags }
  2655.         theFlags := '';
  2656.         IF pFlagTable.EachElementDoTil(DoFlagCase, kIterateForward) = 0 THEN;
  2657.  
  2658.         ch := GetPromptedChar(AtStr('Toggle Flag'), @theFlags, helpProc);
  2659.         CASE ch OF
  2660.             chReturn:
  2661.                 done := TRUE;
  2662.             OTHERWISE
  2663.                 BEGIN
  2664.                 actionCh := ch;
  2665.                 IF pFlagTable.EachElementDoTil(DoFlagAction, kIterateForward) = 0 THEN;
  2666.                 END;
  2667.         END;
  2668.     UNTIL done;
  2669.     END;
  2670.  
  2671. {--------------------------------------------------------------------------------------------------}
  2672. {$S MADebugger}
  2673.  
  2674. PROCEDURE DoFullStop;
  2675.  
  2676.     VAR
  2677.         nubPSN:             ProcessSerialNumber;
  2678.         aERec:                EventRecord;
  2679.         wasFront:            BOOLEAN;
  2680.  
  2681.     BEGIN
  2682.     pMoreMem := - 1;
  2683.     IF IsFrontProcess THEN
  2684.         wasFront := TRUE
  2685.     ELSE
  2686.         wasFront := FALSE;
  2687.  
  2688.     IF wasFront THEN
  2689.         HiliteMenu(mDebug);
  2690.  
  2691.     IF pAtBreak THEN
  2692.         pAtBreak := FALSE;
  2693.  
  2694.     REPEAT
  2695.         NubWaitNextEvent;
  2696.     UNTIL NOT pStoppedInDebugger;
  2697.  
  2698.     { Make sure that we are in the foreground if we were stopped there }
  2699.     IF (NOT gSingleStep) & (pStepOverStackSize = 0) & wasFront THEN
  2700.         BEGIN
  2701.         FailOSErr(GetCurrentProcess(nubPSN));
  2702.         FailOSErr(SetFrontProcess(nubPSN));
  2703.         FailOSErr(WakeUpProcess(nubPSN));
  2704.         WHILE NOT WaitNextEvent(everyEvent, aERec, 1, NIL) DO;
  2705.         HiliteMenu(0);
  2706.         END;
  2707.     END;
  2708.  
  2709. {$EndC}
  2710. {$IFC qDebug}
  2711.  
  2712. {--------------------------------------------------------------------------------------------------}
  2713. {$S MADebugger}
  2714.  
  2715. FUNCTION DebuggerDispatch(message, reply: AppleEvent; info: Longint): OSErr;
  2716.  
  2717.     VAR
  2718.         theEventClass:        AEEventClass;
  2719.         theEventID:         AEEventID;
  2720.  
  2721.         actualType:         DescType;
  2722.         actualSize:         size;
  2723.         typeCode:            DescType;
  2724.         theErr:             OSErr;
  2725.         savedScript:        INTEGER;
  2726.         ch:                 CHAR;
  2727.  
  2728.         error, errMessage:    INTEGER;
  2729.         gotSymbol:            BOOLEAN;
  2730.         myA5:                Longint;
  2731.         myPort:             GrafPtr;
  2732.         i:                    INTEGER;
  2733.         why:                Str255;
  2734.  
  2735.     BEGIN
  2736.     FailOSErr(AEGetAttributePtr(message, keyEventClassAttr, typeType, typeCode, @theEventClass,
  2737.                                 sizeof(AEEventClass), actualSize));
  2738.  
  2739.     FailOSErr(AEGetAttributePtr(message, keyEventIDAttr, typeType, typeCode, @theEventID,
  2740.                                 sizeof(AEEventClass), actualSize));
  2741.  
  2742.     IF theEventClass = 'MADB' THEN
  2743.         IF theEventID = kKeyStroke THEN
  2744.             BEGIN
  2745.             theErr := AEGetParamPtr(message, keyDirectObject, 'char', actualType, @lastCH,
  2746.                                     sizeof(CHAR), actualSize);
  2747.  
  2748.             END
  2749.  
  2750.         ELSE IF theEventID = kEnterMacsBug THEN
  2751.             BEGIN
  2752.             { Save the current script, and set it to Roman for Debugger }
  2753.             savedScript := GetEnvirons(smKeyScript);
  2754.             KeyScript(smRoman);
  2755.  
  2756.             DebugStr('Type ''G'' to return to the MacApp debugger.');
  2757.  
  2758.             KeyScript(savedScript);
  2759.             END
  2760.  
  2761.         ELSE IF theEventID = kExitToShell THEN
  2762.             BEGIN
  2763.             ExitToShell;
  2764.             END
  2765.  
  2766.         ELSE IF theEventID = kTrace THEN
  2767.             BEGIN
  2768.             pTraceToggle := NOT pTraceToggle;
  2769.             gTracing := pTraceToggle & pTraceEnabled;
  2770.             END
  2771.  
  2772.         ELSE IF theEventID = kGo THEN
  2773.             BEGIN
  2774.             pStoppedInDebugger := FALSE;
  2775.             pStepOverStackSize := 0;
  2776.             END
  2777.  
  2778.         ELSE IF theEventID = kStepOver THEN
  2779.             BEGIN
  2780.             pStoppedInDebugger := FALSE;
  2781.             gSingleStep := FALSE;
  2782.             pStepOverStackSize := pStackSpace
  2783.             END
  2784.  
  2785.         ELSE IF theEventID = kStepInto THEN
  2786.             BEGIN
  2787.             pStoppedInDebugger := FALSE;
  2788.             gSingleStep := TRUE;
  2789.             pStepOverStackSize := 0
  2790.             END
  2791.  
  2792.         ELSE IF theEventID = kStatus THEN
  2793.             BEGIN
  2794.             { Put the pointer data in as direct parameter… }
  2795.             myA5 := GetA5;
  2796.             GetPort(myPort);
  2797.             FailOSErr(AEPutParamPtr(reply, 'A5  ', typeLongInteger, @myA5, sizeof(Longint)));
  2798.             FailOSErr(AEPutParamPtr(reply, 'port', typeLongInteger, @myPort, sizeof(Longint)));
  2799.             FailOSErr(AEPutParamPtr(reply, 'trac', 'bool', @pTraceToggle, sizeof(BOOLEAN)));
  2800. {$Ifc qPerform}
  2801.             FailOSErr(AEPutParamPtr(reply, 'perf', 'bool', @oldState, sizeof(BOOLEAN)));
  2802. {$Endc}
  2803.  
  2804.             {###
  2805.             IF pBreakCount > 0 THEN
  2806.                 BEGIN
  2807.                 Write('Break[s] set at: ');
  2808.                 FOR i := 1 TO pBreakCount DO
  2809.                     BEGIN
  2810.                     IF i > 1 THEN
  2811.                         Write(', ');
  2812.                     IF pBreakClass[i] <> '' THEN
  2813.                         Write(pBreakClass[i], '.', pBreakProc[i])
  2814.                     ELSE
  2815.                         Write(pBreakProc[i]);
  2816.                     END;
  2817.                 END
  2818.             ELSE
  2819.                 Write('No Break set.');
  2820.             }
  2821.             CASE gWhyInDebugger OF
  2822.                 tBegin:
  2823.                     why := 'Begin  ';
  2824.                 tEnd:
  2825.                     why := 'End    ';
  2826.                 tExit:
  2827.                     why := 'Exit   ';
  2828.                 tBeginEndPair:
  2829.                     why := 'BegEnd ';
  2830.                 tSysError:
  2831.                     why := 'SysErr ';
  2832.                 tProgBreak:
  2833.                     why := 'Break  ';
  2834.                 tVBL:
  2835.                     why := 'VBL Break  ';
  2836.             END;
  2837.  
  2838.             FailOSErr(AEPutParamPtr(reply, 'WhyI', 'S255', @why, length(why) + 1));
  2839.             FailOSErr(AEPutParamPtr(reply, 'pnam', 'S255', @procName, length(procName) + 1));
  2840.             FailOSErr(AEPutParamPtr(reply, 'seg#', typeShortInteger, @segNum, sizeof(INTEGER)));
  2841.             IF ord(receiver) > 0 THEN
  2842.                 BEGIN
  2843.                 FailOSErr(AEPutParamPtr(reply, 'recv', typeLongInteger, @rcvrHandle,
  2844.                                         sizeof(INTEGER)));
  2845.                 FailOSErr(AEPutParamPtr(reply, 'recC', 'S255', @rcvrClass, length(rcvrClass) + 1));
  2846.                 END;
  2847.  
  2848.             END
  2849.  
  2850.         ELSE IF theEventID = kAllClasses THEN
  2851.             BEGIN
  2852.             {### AllClassesCmd;}
  2853.             END
  2854.  
  2855.         ELSE IF theEventID = kDisplayMem THEN
  2856.             BEGIN
  2857.             IF GetPromptedNumber(AtStr('Display memory starting where?: '), asDecimal, asHex) THEN
  2858.                 IF asHex <> - 1 THEN
  2859.                     ShowMemory(asHex, 16);
  2860.             END
  2861.  
  2862.         ELSE IF theEventID = kDisasm THEN
  2863.             BEGIN
  2864. {$IFC IncludeDisassembler}
  2865.             IF GetPromptedNumber(AtStr('Disassemble memory starting where?: '), asDecimal,
  2866.                asHex) THEN
  2867.                 IF asHex <> - 1 THEN
  2868.                     ShowDisasmMemory(asHex, 16);
  2869. {$EndC}
  2870.             END
  2871.         ELSE IF theEventID = kFieldsAsHex THEN
  2872.             BEGIN
  2873.             IF GetPromptedValue(AtStr('Fields of object [hex handle, or decimal stack level #]?: '),
  2874.                                 asDecimal, asHex, TRUE, gotSymbol) THEN
  2875.                 IF (asDecimal >= 0) & (asDecimal < 100) & NOT gotSymbol THEN
  2876.                     ShowFields(GetRcvrAtLevel(asDecimal, pLink), FALSE)
  2877.                 ELSE
  2878.                     ShowFields(TObject(asHex), FALSE);
  2879.             END
  2880.  
  2881.         ELSE IF theEventID = kInspect THEN
  2882.             BEGIN
  2883.             IF GetPromptedValue(AtStr(
  2884.                                      'Inspect what object [hex handle, or decimal stack level #]?: '
  2885.                                       ), asDecimal, asHex, TRUE, gotSymbol) THEN
  2886.                 IF (asDecimal >= 0) & (asDecimal < 100) & NOT gotSymbol THEN
  2887.                     ShowFields(GetRcvrAtLevel(asDecimal, pLink), TRUE)
  2888.                 ELSE
  2889.                     ShowFields(TObject(asHex), TRUE);
  2890.             END
  2891.  
  2892.         ELSE IF theEventID = kLocals THEN
  2893.             BEGIN
  2894.             IF GetPromptedNumber(AtStr('Local variables of procedure [stack level #]?: '),
  2895.                                  asDecimal, asHex) THEN
  2896.                 IF asDecimal <> - 1 THEN
  2897.                     ShowLocals(asDecimal, pLink);
  2898.             END
  2899.  
  2900.         ELSE IF theEventID = kMore THEN
  2901.             BEGIN
  2902.             IF pMoreMem = - 1 THEN
  2903.                 WriteLn('There is no more to show.')
  2904.             ELSE
  2905.                 ShowMemory(pMoreMem, 16);
  2906.             END
  2907.  
  2908.         ELSE IF theEventID = kMoreDisasm THEN
  2909.             BEGIN
  2910. {$IFC IncludeDisassembler}
  2911.             IF pMoreMem = - 1 THEN
  2912.                 WriteLn('There is no more to show.')
  2913.             ELSE
  2914.                 ShowDisasmMemory(pMoreMem, 16);
  2915. {$EndC}
  2916.             END
  2917.  
  2918.         ELSE IF theEventID = kParameters THEN
  2919.             BEGIN
  2920.             IF GetPromptedNumber(AtStr('Parameters of procedure [stack level #]?: '), asDecimal,
  2921.                                  asHex) THEN
  2922.                 IF asDecimal <> - 1 THEN
  2923.                     ShowParameters(asDecimal, pLink);
  2924.             END
  2925.  
  2926.         ELSE IF theEventID = kRecentPC THEN
  2927.             ShowRecent
  2928.  
  2929.         ELSE IF theEventID = kStack THEN
  2930.             BEGIN
  2931.             nextLevel := 0;
  2932.             nextFrame := pLink;
  2933.             pNextPC := ppc;
  2934.             ShowStack;
  2935.             END
  2936.  
  2937.         ELSE IF theEventID = kSignalFailure THEN
  2938.             BEGIN
  2939.             { Get ready to blow out of debugger ### move this out to the outside of AE }
  2940.             IF GetPromptedNumber(AtStr('Error to signal with Failure?: '), asDecimal, asHex) THEN
  2941.                 BEGIN
  2942.                 error := asDecimal;
  2943.                 IF GetPromptedNumber(AtStr('Message to signal with Failure?: '), asDecimal,
  2944.                    asHex) THEN
  2945.                     BEGIN
  2946.                     errMessage := asDecimal;
  2947.                     gReportNext := FALSE;
  2948.  
  2949.                     { Blow }
  2950.                     Failure(error, errMessage);
  2951.                     END;
  2952.                 END;
  2953.             END
  2954.  
  2955.         ELSE IF theEventID = kSetBreak THEN
  2956.             SetBreakCmd
  2957.  
  2958.         ELSE IF theEventID = kClearBreak THEN
  2959.             ClrBreakCmd
  2960.  
  2961.         ELSE IF theEventID = kHeapCmd THEN
  2962.             HeapCmd
  2963.  
  2964.         ELSE IF theEventID = kPerfCommand THEN
  2965.             BEGIN
  2966. {$Ifc qPerform}
  2967.             PerfCmd
  2968. {$Endc}
  2969.             END
  2970.  
  2971.         ELSE IF theEventID = kFlags THEN
  2972.             FlagCmd;
  2973.  
  2974.     DebuggerDispatch := NoErr;
  2975.     END;
  2976.  
  2977. {--------------------------------------------------------------------------------------------------}
  2978. {$S MADebugger}
  2979.  
  2980. PROCEDURE NubWaitNextEvent;
  2981.  
  2982.     VAR
  2983.         theEvent:             EventRecord;
  2984.         event:                TToolBoxEvent;
  2985.  
  2986.     BEGIN
  2987.     { Yield and get commands from debugger }
  2988.     IF WaitNextEvent(everyEvent, theEvent, kMaxIdleTime, GetGrayRgn) THEN
  2989.         BEGIN
  2990.         CASE theEvent.what OF
  2991.             kHighLevelEvent:
  2992.                 FailOSErr(AEProcessAppleEvent(theEvent));
  2993.             OTHERWISE
  2994.                 IF YouAreWarned & IsFrontProcess THEN
  2995.                     BEGIN
  2996.                     New(event);
  2997.                     event.IToolBoxEvent(NIL);
  2998.                     event.HaveEvent(theEvent);
  2999.                     event.Process;
  3000.                     END;
  3001.         END;
  3002.         END;
  3003.     END;
  3004.  
  3005. {--------------------------------------------------------------------------------------------------}
  3006. {$S MADebugger}
  3007.  
  3008. PROCEDURE MADebuggerMainEntry(aWhyInDebugger: WhyInDebugger; aPLink, aPpc: Longint);
  3009.  
  3010.     VAR
  3011.         i: INTEGER;
  3012.         forgotSuccess: BOOLEAN;
  3013.         aWho: MAName;
  3014.         pc: Longint;
  3015.         anERec: EventRecord;
  3016.  
  3017.         theMessage:         AEDesc;
  3018.         theReply:            AEDesc;
  3019.  
  3020.  
  3021.     BEGIN
  3022.     IF NOT pCanEnterDebugger THEN { debugger is not re-entrant. But give user a fighting chance }
  3023.         DebugStr('Re-entering the non re-entrant MacApp debugger. Proceed with care!')
  3024.     ELSE
  3025.         pCanEnterDebugger := FALSE;
  3026.  
  3027.     { make the reason we're here available to other procs }
  3028.     gWhyInDebugger := aWhyInDebugger;
  3029.     pLink := aPLink;
  3030.     ppc := aPpc;
  3031.  
  3032.     pRecentIndex := BAND(pRecentIndex + 1, kRecent); { modulo kRecent }
  3033.     WITH pRecentPC[pRecentIndex] DO
  3034.         BEGIN
  3035.         thePC := LongIntPtr(ppc)^;
  3036.         theWhyInDebugger := gWhyInDebugger;
  3037.         END;
  3038.  
  3039.     IF gMastReport THEN
  3040.         CheckFreeMasters
  3041.     ELSE
  3042.         pMasters := - 1;
  3043.  
  3044.     stkBreak := (gWhyInDebugger = tBegin) & ((pStackSpace > pBreakStack) | (pProcStack >
  3045.                 pBrProcStack));
  3046.     stepBreak := (pStackSpace <= pStepOverStackSize); { stop only if stack is same or less for
  3047.                                                        single stepping }
  3048.  
  3049.     IF pBreakCount > 0 THEN
  3050.         BEGIN
  3051.         GetProcName(ppc, className, procName);
  3052.         IF length(className) > 0 THEN
  3053.             Delete(procName, 1, length(className) + 1);
  3054.  
  3055.         FOR i := 1 TO pBreakCount DO
  3056.             BEGIN
  3057.             pAtBreak := ((length(pBreakClass[i]) = 0) | (pBreakClass[i] = className)) & (
  3058.                         (length(pBreakProc[i]) <> 0) & (pBreakProc[i] = procName));
  3059.             IF pAtBreak THEN
  3060.                 LEAVE;
  3061.             END;
  3062.         END
  3063.     ELSE
  3064.         pAtBreak := stkBreak | stepBreak;
  3065.  
  3066.     pStoppedInDebugger := gSingleStep | pAtBreak | (gWhyInDebugger >= tProgBreak) | IsUserBreak;
  3067.  
  3068.     { Check to see if we have too few calls to Success when leaving a procedure. This might be
  3069.     the case if the user forgot to make the call or it was missed and the handler is on the stack,
  3070.     which it usually (??? always) is. }
  3071.     forgotSuccess := ((gWhyInDebugger = tEnd) | (gWhyInDebugger = tExit)) & (gTopHandler <> NIL) &
  3072.                      (LongIntPtr(pLink)^ >= Longint(gTopHandler));
  3073.     IF forgotSuccess THEN
  3074.         BEGIN
  3075.         WriteLn(
  3076.            'You''re leaving a routine without calling Success for a handler that will be destroyed.'
  3077.                 );
  3078.         pc := longint(gTopHandler^.exceptionHandler);
  3079.         GetMethodName(Longint(@pc), aWho);
  3080.         WriteLn('Failure handler is: ', aWho);
  3081.         pStoppedInDebugger := TRUE;
  3082.         END;
  3083.  
  3084.     IF gTracing | gReportNext | pStoppedInDebugger THEN
  3085.         BEGIN
  3086.         IF gReportNext & (length(gReportInfo) <> 0) THEN
  3087.             BEGIN
  3088.             WriteLn(gReportInfo);
  3089.             gReportInfo := '';
  3090.             END;
  3091.  
  3092.         IF TrcEnable(TRUE) THEN;
  3093.  
  3094.         IF NOT pStoppedInDebugger & gReportTime THEN
  3095.             Write(TickCount: 10, ': ');
  3096.  
  3097.         IF pAtBreak THEN
  3098.             BEGIN
  3099.             IF stkBreak THEN
  3100.                 Write('(stack space) ');
  3101.             Write('broke at ');
  3102.             END
  3103.         ELSE IF gReportNext THEN
  3104.             Write('@ ')
  3105.         ELSE IF pStoppedInDebugger THEN
  3106.             Write('stopped at ');
  3107.  
  3108.         GetFrameInfo(pLink, ppc, callerFrame, itsFrame, receiver, className, procName, rcvrHandle,
  3109.                      rcvrClass, segNum);
  3110.  
  3111.         ShowWhyInDebugger(gWhyInDebugger, procName, segNum);
  3112.         IF ord(receiver) > 0 THEN
  3113.             Write('  Self: ', rcvrHandle, ' is ', rcvrClass);
  3114.         WriteLn;
  3115.  
  3116.  
  3117.         IF pStoppedInDebugger THEN
  3118.             BEGIN
  3119.             { notify debugger that we're stopping }
  3120.             { Create the basic message to send }
  3121.             FailOSErr(AECreateAppleEvent('MADB', kEnteredDebugger, NubGetDebuggerAddress,
  3122.                                          kAutoGenerateReturnID, kAnyTransactionID, theMessage));
  3123.         
  3124.             { Send it off, and don't worry about a reply or receipt }
  3125.             FailOSErr(AESend(theMessage, theReply, kAENoReply, kAENormalPriority, 1000, NIL, NIL));
  3126.         
  3127.             FailOSErr(AEDisposeDesc(theMessage));
  3128.  
  3129.  
  3130. {###SRF         if FALSE & (pEnterProc <> NIL) THEN
  3131.                 CallEnter(TRUE, pEnterProc);}
  3132.  
  3133.             {$Ifc qPerform}
  3134.             oldState := DebugPerfMonitor(FALSE);
  3135.             {$Endc}
  3136.  
  3137.             WithHideFromMacAppDo(DoFullStop, FullHide);
  3138.  
  3139. {###SRF         if FALSE & (pEnterProc <> NIL) THEN
  3140.                 CallEnter(FALSE, pEnterProc);}
  3141.  
  3142.             {$Ifc qPerform}
  3143.             IF DebugPerfMonitor(oldState) THEN;
  3144.             {$Endc}
  3145.             END
  3146.         ELSE IF EventAvail(everyEvent, anERec) THEN; { share time so tracing shows up }
  3147.  
  3148.         END;
  3149.  
  3150.     gReportNext := FALSE;
  3151.  
  3152.     pCanEnterDebugger := TRUE;
  3153.  
  3154.     END;
  3155.  
  3156. {--------------------------------------------------------------------------------------------------}
  3157. {$S Main}
  3158. {$Push} {$Z+} {$%+}
  3159.  
  3160. PROCEDURE %_BP;
  3161.  
  3162.     VAR
  3163.         OldA5:                Longint;
  3164.  
  3165.     BEGIN
  3166.     OldA5 := SetCurrentA5;
  3167.     IF pCanEnterDebugger THEN
  3168.         BEGIN
  3169.         pStackSpace := ord(GetCurStackBase) - ord(GetCurStackTop);
  3170.         IF pStackSpace > gMaxStackDepth THEN
  3171.             gMaxStackDepth := pStackSpace;
  3172.  
  3173.         pProcStack := LongIntPtr(GetCurStackFramePtr)^ - Longint(GetCurStackFramePtr) - 8;
  3174.  
  3175.         MADebuggerMainEntry(tBegin, Ord4(GetCurStackFramePtr), Ord4(GetCurStackFramePtr) + 4);
  3176.         END;
  3177.     OldA5 := SetA5(OldA5);
  3178.     END;
  3179. {$Pop}
  3180.  
  3181. {--------------------------------------------------------------------------------------------------}
  3182. {$S Main}
  3183. {$Push} {$Z+} {$%+}
  3184.  
  3185. PROCEDURE %_EP;
  3186.  
  3187.     VAR
  3188.         OldA5:                Longint;
  3189.  
  3190.     BEGIN
  3191.     OldA5 := SetCurrentA5;
  3192.     IF pCanEnterDebugger THEN
  3193.         BEGIN
  3194.         pStackSpace := ord(GetCurStackBase) - ord(GetCurStackTop);
  3195.         MADebuggerMainEntry(tEnd, Ord4(GetCurStackFramePtr), Ord4(GetCurStackFramePtr) + 4);
  3196.         END;
  3197.     OldA5 := SetA5(OldA5);
  3198.     END;
  3199. {$Pop}
  3200.  
  3201. {--------------------------------------------------------------------------------------------------}
  3202. {$S Main}
  3203. {$Push} {$Z+} {$%+}
  3204.  
  3205. PROCEDURE %_EX;
  3206.  
  3207.     VAR
  3208.         OldA5:                Longint;
  3209.  
  3210.     BEGIN
  3211.     OldA5 := SetCurrentA5;
  3212.     IF pCanEnterDebugger THEN
  3213.         BEGIN
  3214.         pStackSpace := ord(GetCurStackBase) - ord(GetCurStackTop);
  3215.         MADebuggerMainEntry(tExit, Ord4(GetCurStackFramePtr), Ord4(GetCurStackFramePtr) + 4);
  3216.         END;
  3217.     OldA5 := SetA5(OldA5);
  3218.     END;
  3219. {$Pop}
  3220.  
  3221. {--------------------------------------------------------------------------------------------------}
  3222. {$S MADebugger}
  3223.  
  3224. PROCEDURE EnterMacAppDebugger;                            { called by ProgramBreak in UOBJECT }
  3225.  
  3226.     VAR
  3227.         notADummy:            Longint;
  3228.  
  3229.     BEGIN
  3230.     notADummy := LongIntPtr(Ord4(GetCurStackFramePtr))^; { they called ProgramBreak called
  3231.                                                           EnterMacAppDebugger: skip a level }
  3232.     MADebuggerMainEntry(tProgBreak, notADummy, notADummy + 4);
  3233.     END;
  3234.  
  3235. {--------------------------------------------------------------------------------------------------}
  3236. {$S MADebugger}
  3237. {$Push} {$Z+}
  3238.  
  3239. FUNCTION GetErrTxt(errorCode: INTEGER): Str255;
  3240.  
  3241.     BEGIN
  3242.     GetIndString(GetErrTxt, 252, errorCode);
  3243.     END;
  3244. {$Pop}
  3245.  
  3246. {--------------------------------------------------------------------------------------------------}
  3247. {$S MADebugger}
  3248. {$Push} {$Z+}
  3249.  
  3250. VAR
  3251.     e:                    Str255;
  3252.  
  3253. PROCEDURE DebugException(errorCode: INTEGER);
  3254. { 68000 exceptions (code 901-910) and SysError calls }
  3255.  
  3256.     CONST
  3257.         kUnInitStorage1     = $72677267;                { Pascal provided uninited storage }
  3258.         kUnInitStorage2     = $67726772;                { odd byte boundary of above }
  3259.         kDebugHandleInit    = $F3F3F3F3;                { Handles are inited to this in MacApp® }
  3260.         kDebugPtrInit        = $F5F5F5F5;                { Pointers are inited to this in MacApp® }
  3261.         kDebugObjInit        = $F1F1F1F1;                { Objects are inited to this in MacApp® }
  3262.  
  3263.     VAR
  3264.         notADummy:            Longint;
  3265.         accessAddr:         Longint;
  3266.         extras:             INTEGER;
  3267.         OldA5:                Longint;
  3268.         oldResLoad:        BOOLEAN;
  3269.         oldResFile:        INTEGER;
  3270.  
  3271.     BEGIN
  3272.     OldA5 := SetCurrentA5;
  3273.     oldResLoad := GetResLoad;
  3274.     SetResLoad(TRUE);
  3275.     oldResFile := MAUseResFile(gApplicationRefNum);
  3276.  
  3277.     notADummy := ord(@notADummy) + 78;                    { Where to leave continuation address =
  3278.                                                          dummy4+link4+pc4+arg2+16*reg4 }
  3279.     LongIntPtr(notADummy)^ := pSysErrPatch.oldTrapAddr; { Tentative value (worst case & disk
  3280.                                                          inserts) }
  3281.  
  3282.     IF (errorCode = - 127) |                            { Old menu not found. }
  3283.        (errorCode = - 126) |                            { Old menu bar not found. }
  3284.        (errorCode = 30) |                                { "Please insert the disk". }
  3285.        ((errorCode >= 50) & (errorCode <= 69)) |        { SADE }
  3286.        ((errorCode >= $7FF0) & (errorCode <= $7FFF))    { Reserved for system or app use. }
  3287.        THEN
  3288.         BEGIN
  3289.         { Drop through }
  3290.         END
  3291.     ELSE
  3292.         BEGIN
  3293.         IF NOT pCanEnterDebugger THEN
  3294.             DebugStr(
  3295.                 'Re-entering the non re-entrant MacApp debugger ON AN EXCEPTION. Proceed with care!'
  3296.                      );
  3297.  
  3298.         { If an exception happens in the exception handler, give up! }
  3299.         InstallExceptionHandlers(FALSE);
  3300.  
  3301.         EmptyHandle(pReserve);                            { we need all the space we can get }
  3302.  
  3303.         WriteLn;
  3304.  
  3305.         extras := 0;
  3306.         accessAddr := 0;
  3307.         IF (errorCode DIV 100) = 9 THEN                 { 900-9xx are 68000 exceptions, not SysErr
  3308.                                                          calls }
  3309.             BEGIN
  3310.             { Where to go after this procedure returns }
  3311.             CASE (errorCode - 900) * sizeof(Longint) OF
  3312.                 exBusError:
  3313.                     Handle(notADummy)^ := pOldexBusError;
  3314.                 exAddressError:
  3315.                     Handle(notADummy)^ := pOldexAddressError;
  3316.                 exIllegalInst:
  3317.                     Handle(notADummy)^ := pOldexIllegalInst;
  3318.                 exZeroDivide:
  3319.                     Handle(notADummy)^ := pOldexZeroDivide;
  3320.                 exCheck:
  3321.                     Handle(notADummy)^ := pOldexCheck;
  3322.                 exOverflow:
  3323.                     Handle(notADummy)^ := pOldexOverflow;
  3324.                 exLineF:
  3325.                     Handle(notADummy)^ := pOldexLineF;
  3326.             END;
  3327.  
  3328.             IF errorCode = 900 THEN
  3329.                 Write('NMI Button: ')
  3330.             ELSE
  3331.                 Write('Exception #', errorCode - 900: 1, '  ');
  3332.             errorCode := errorCode - 901;
  3333.             { Thanks to Rob Hawley for improvements to the following code }
  3334.             IF (errorCode = 1) | (errorCode = 2) | (errorCode = 3) | (errorCode = 6) THEN { Bus
  3335.                    error or Address error }
  3336.                 BEGIN
  3337.                 { 68000 and 68020 have different exception stack frames }
  3338.                 IF NOT (qNeedsMC68020 | qNeedsMC68030) & (gConfiguration.processor =
  3339.                    gestalt68000) THEN
  3340.                     BEGIN
  3341.                     extras := 8;                        { 68000 precedes status and PC with 4 words
  3342.                                                          }
  3343.                     accessAddr := LongIntPtr(notADummy + 6)^; { which includes the access address }
  3344.                     END
  3345.                 ELSE
  3346.                     BEGIN
  3347.                     extras := 0;                        { no extra stack frame data before status
  3348.                                                          reg & PC }
  3349.                     wrlblptr('exception frame Addr', LongIntPtr(notADummy + 4));
  3350.                     WriteLn;
  3351.                     IF (errorCode = 1) | (errorCode = 2) THEN
  3352.                         BEGIN
  3353.                         wrlblptr('PC', LongIntPtr(notADummy + 4 + 2)^);
  3354.                         WriteLn;
  3355.                         accessAddr := LongIntPtr(notADummy + 20)^; { Must add 16 - 4 to get
  3356.                                                                     offending address}
  3357.                         END
  3358.                     ELSE
  3359.                         accessAddr := LongIntPtr(notADummy + 4 + 2)^; {Same as PC}
  3360.                     END
  3361.                 END
  3362.             END
  3363.         ELSE
  3364.             Write('SysErr ID = ', errorCode: 1, '  ');
  3365.  
  3366.         CASE errorCode OF                                { All SysError argument values except where
  3367.                                                          indicated }
  3368.             0..28:
  3369.                 e := GetErrTxt(errorCode + 1);
  3370.             33:
  3371.                 e := GetErrTxt(30);
  3372.             { 30, 31: ...Disk insert... }
  3373.             41:
  3374.                 e := GetErrTxt(31);
  3375.             42:
  3376.                 e := GetErrTxt(32);
  3377.             51:
  3378.                 e := GetErrTxt(33);
  3379.             81:
  3380.                 e := GetErrTxt(34);
  3381.             84:
  3382.                 e := GetErrTxt(35);
  3383.             85:
  3384.                 e := GetErrTxt(36);
  3385.             86:
  3386.                 e := GetErrTxt(37);
  3387.             100:
  3388.                 e := GetErrTxt(38);
  3389.             MAXINT:
  3390.                 e := GetErrTxt(39);
  3391.             OTHERWISE
  3392.                 IF (32 <= errorCode) & (errorCode <= 53) THEN
  3393.                     e := GetErrTxt(40)
  3394.                 ELSE
  3395.                     e := GetErrTxt(41);
  3396.         END;
  3397.  
  3398.         WriteLn(e);
  3399.         IF accessAddr <> 0 THEN
  3400.             BEGIN
  3401.             Write('Bad address was: ');
  3402.             WritePtr(accessAddr);
  3403.             WriteLn;
  3404.             IF accessAddr = kUnInitStorage1 THEN
  3405.                 WriteLn('Appears to be Pascal provided uninitialized storage.')
  3406.             ELSE IF accessAddr = kUnInitStorage2 THEN
  3407.                 WriteLn(
  3408.                       'Appears to be Pascal provided uninitialized storage at an odd byte boundary.'
  3409.                         )
  3410.             ELSE IF accessAddr = kDebugHandleInit THEN
  3411.                 WriteLn('Appears to be Handle contents initialized by debugging.')
  3412.             ELSE IF accessAddr = kDebugPtrInit THEN
  3413.                 WriteLn('Appears to be Pointer contents initialized by debugging.')
  3414.             ELSE IF accessAddr = kDebugObjInit THEN
  3415.                 WriteLn('Appears to be uninitialized instance variable.')
  3416.             END;
  3417. {###SRF        gApplication.Beep(30);    }                        { 1/2 second }
  3418.  
  3419.         MADebuggerMainEntry(tSysError, Ord4(GetCurStackFramePtr), Ord4(GetCurStackFramePtr) +
  3420.                             2 + extras);
  3421. {###SRF                InstallExceptionHandlers(TRUE);}
  3422.         END;
  3423.     IF MAUseResFile(oldResFile) = 0 THEN;
  3424.     SetResLoad(oldResLoad);
  3425.     OldA5 := SetA5(OldA5);
  3426.     END;
  3427. {$Pop}
  3428.  
  3429. {--------------------------------------------------------------------------------------------------}
  3430. {$Push} {$IFC qTrace} {$D+} {$ENDC}
  3431. {$W+}
  3432. {$R-}
  3433. {$Init-}
  3434. {$OV-}
  3435. {$S MADebugger}
  3436.  
  3437. PROCEDURE aVBLTask;
  3438.  
  3439.     CONST
  3440.         kVBLDelay            = 15;                        { Ticks before checking }
  3441.         theOffset            = sizeof(Longint) * 2;
  3442.  
  3443.     VAR
  3444.         aKeyMap:            KeyMap;
  3445.         oldState:            INTEGER;
  3446.  
  3447.     BEGIN
  3448.  
  3449. { Set up application's A5.
  3450.   Our A5 is prepended to the QElem which is pointed at by A0 }
  3451.  
  3452.     WITH pVBLInfo DO
  3453.         pVBLInfo.aQElemWithA5.OldA5 := SetA5(VBLInfoPtr(GetParmBlockPtr - theOffset)^.aQElemWithA5.
  3454.                                              A5);
  3455.  
  3456.     oldState := IntegerPtr(JournalFlag)^;
  3457.     IntegerPtr(JournalFlag)^ := 0;                        { turn off journaling }
  3458.     GetKeys(aKeyMap);
  3459.     IntegerPtr(JournalFlag)^ := oldState;
  3460.  
  3461.     IF aKeyMap[59] & aKeyMap[55] & aKeyMap[56] & aKeyMap[58] & pCanEnterDebugger THEN
  3462.         MADebuggerMainEntry(tVBL, Ord4(GetCurStackFramePtr), Ord4(GetCurStackFramePtr) + 4);
  3463.  
  3464.     { always Reset the vblCount }
  3465.     WITH pVBLInfo DO
  3466.         BEGIN
  3467.         aQElemWithA5.q.vblQElem.vblCount := kVBLDelay;
  3468.         IF SetA5(aQElemWithA5.OldA5) = 0 THEN;            { discard the function result }
  3469.         END;
  3470.  
  3471.     END;
  3472. {$Pop}
  3473.  
  3474. {--------------------------------------------------------------------------------------------------}
  3475. {$S MAInit}
  3476.  
  3477. PROCEDURE VBLInstall;
  3478.  
  3479.     CONST
  3480.         kVBLDelay            = 15;                        { Ticks before checking }
  3481.  
  3482.     BEGIN
  3483.     IF pInterceptExceptionVectors THEN
  3484.         WITH pVBLInfo DO
  3485.             BEGIN
  3486.             { Setup the VBL task }
  3487.             WITH aQElemWithA5.q.vblQElem DO
  3488.                 BEGIN
  3489.                 qType := ord(vType);
  3490.                 vblAddr := @aVBLTask;
  3491.                 vblCount := kVBLDelay;
  3492.                 vblPhase := 0;
  3493.                 END;
  3494.             aQElemWithA5.A5 := Longint(GetA5);
  3495.             { This will make the A5 world available to the VBL task }
  3496.  
  3497.             { Install the VBL task }
  3498.             FailOSErr(VInstall(@aQElemWithA5.q));
  3499.             END;
  3500.     END;
  3501.  
  3502. {--------------------------------------------------------------------------------------------------}
  3503. {$S MADebugger}
  3504.  
  3505. PROCEDURE VBLRemove;
  3506.  
  3507. { removes the VBL task }
  3508.  
  3509.     VAR
  3510.         e:                    OSErr;
  3511.  
  3512.     BEGIN
  3513.     IF pInterceptExceptionVectors THEN
  3514.         e := VRemove(@pVBLInfo.aQElemWithA5.q);         { Discard error }
  3515.     END;
  3516.  
  3517. {--------------------------------------------------------------------------------------------------}
  3518. {$S MADebugger}
  3519.  
  3520. PROCEDURE DebugEndForce;
  3521.  
  3522.     BEGIN
  3523.     END;
  3524.  
  3525. {--------------------------------------------------------------------------------------------------}
  3526. {$S MADebugger}
  3527.  
  3528. PROCEDURE DebugForceOutput(DebugToWindow, DebugToFile: DebugForceOptions);
  3529.  
  3530.     BEGIN
  3531.     END;
  3532.  
  3533. PROCEDURE InitUDebugAfterIApplication;
  3534. { Call this once at the end of IApplication to finish initialization of the debugger. }
  3535.  
  3536.     BEGIN
  3537.     END;
  3538.  
  3539. {--------------------------------------------------------------------------------------------------}
  3540. {$S MADebugger}
  3541.  
  3542. PROCEDURE DebugShowTranscriptWindow;
  3543. { Call this proc from MacApp to show the window }
  3544.  
  3545.     BEGIN
  3546.     END;
  3547.  
  3548. {--------------------------------------------------------------------------------------------------}
  3549. {$S MADebugger}
  3550.  
  3551. FUNCTION DebugCapture(captureProc: ProcPtr): ProcPtr;
  3552. { Install an alternative capture proc, which will get called for every
  3553. writeln. It should have the same interface as AddText. You will
  3554. probably want to set gWrToWindow to FALSE to inhibit output to the
  3555. window at the same time. Pass NIL to remove any capture proc. }
  3556.  
  3557.     BEGIN
  3558.     DebugCapture := fCaptureProc;
  3559.     fCaptureProc := captureProc;
  3560.     END;
  3561.  
  3562. {$EndC qDebug}
  3563. {--------------------------------------------------------------------------------------------------}
  3564. {$S Main}
  3565.  
  3566. FUNCTION DebugCanReadLn: BOOLEAN;
  3567. { Returns True if you can readln to the user }
  3568.  
  3569.     BEGIN
  3570.     DebugCanReadLn := pUDebugInitialized;
  3571.     END;
  3572.  
  3573. {--------------------------------------------------------------------------------------------------}
  3574. {$S Main}
  3575.  
  3576. FUNCTION DebugCanWriteLn: BOOLEAN;
  3577. { Returns True if you can writeln to the user }
  3578.  
  3579.     BEGIN
  3580.     DebugCanWriteLn := pUDebugInitialized;
  3581.     END;
  3582.  
  3583. {--------------------------------------------------------------------------------------------------}
  3584. {$S Main}
  3585.  
  3586. PROCEDURE GetCallersMethodName(VAR s: MAName);
  3587.  
  3588.     BEGIN
  3589.     GetMethodName(LongIntPtr(GetCurStackFramePtr)^ + 4, s); { report about our caller's caller }
  3590.     END;
  3591.  
  3592. {--------------------------------------------------------------------------------------------------}
  3593. {$S Main}
  3594.  
  3595. PROCEDURE GetMethodName(ppc: Longint; VAR s: MAName);
  3596. { GetMethodName returns the name of the method (or procedure) in
  3597. which ppc points. }
  3598.  
  3599.     BEGIN
  3600.     GetProcName(ppc, discardStr, s);
  3601.     END;
  3602.  
  3603. {--------------------------------------------------------------------------------------------------}
  3604. {$S Main}
  3605.  
  3606. PROCEDURE GetProcName(ppc: Longint; VAR className, procName: MAName);
  3607. { GetProcName returns the name of the procedure or function in
  3608. which ppc points.  If it is in a method, then it return's
  3609. the name of the method's class in className. }
  3610.  
  3611.     VAR
  3612.         pc, nextPC, limit:    Ptr;
  3613.         index:                INTEGER;
  3614.  
  3615.     BEGIN
  3616.     pc := Handle(ppc)^;
  3617.     IF (ord(pc) <> 0) & NOT Odd(ord(pc)) THEN
  3618.         BEGIN
  3619.         limit := Ptr(ord(pc) + 32767);
  3620.         WHILE (endOfModule(pc, limit, @procName, nextPC) = NIL) DO
  3621.             BEGIN
  3622.             IF ord(pc) >= ord(limit) THEN
  3623.                 BEGIN
  3624.                 className := '';
  3625.                 procName := '';
  3626.                 LEAVE;
  3627.                 END
  3628.             ELSE
  3629.                 pc := Ptr(ord(pc) + 2);
  3630.             END;
  3631.  
  3632.         index := pos('.', procName);
  3633.         IF index <> 0 THEN
  3634.             BEGIN
  3635.             className := copy(procName, 1, index - 1);
  3636.             END
  3637.         ELSE
  3638.             className := '';
  3639.         END
  3640.     ELSE
  3641.         BEGIN
  3642.         className := '';
  3643.         procName := '';
  3644.         END;
  3645.     END;
  3646.  
  3647. {--------------------------------------------------------------------------------------------------}
  3648. {$S MADebugger}
  3649.  
  3650. FUNCTION TrcEnable(okToTrace: BOOLEAN): BOOLEAN;
  3651. { Control whether tracing from %_BP/%_EP/%_EX is enabled or not.  Set to false when the section
  3652. of code that you are using doesn't really need to be traced (like the inspector or debugger itself).}
  3653.  
  3654.     BEGIN
  3655.     TrcEnable := pTraceEnabled;
  3656.     pTraceEnabled := okToTrace;
  3657.     gTracing := pTraceToggle & pTraceEnabled;
  3658.     END;
  3659.